Java并发编程——深入理解线程池

317 阅读3分钟

线程池核心:三大方法,七大参数,四种拒绝策略

池化技术:

  • 程序的运行的本质:占用系统的资源!优化资源的使用,可以考虑使用池化技术
  • 池化技术:事先准备好一些资源,有人要用,就来这里拿,用完之后还给我

线程池的好处:

  • 降低资源的消耗
  • 提高响应速度
  • 方便管理

线程的复用,可以控制最大并发数,管理线程

阿里巴巴java开发手册中对于线程池的生成是这样规定的: image-20200719123429927

一、创建线程池的三大方法

public class ExecutorDemo {
    public static void main(String[] args) throws InterruptedException {
        //单个线程池
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //创建一个固定大小的线程池
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5);
        //可伸缩的线程池
        ExecutorService threadPool2 = Executors.newCachedThreadPool();
        System.out.println("单一的线程池");
        for (int i = 0; i <10 ; i++) {
            threadPool.execute(()->{
                System.out.println(Thread.currentThread().getName()+" ok");
            });
        }
        TimeUnit.SECONDS.sleep(2);
        System.out.println("固定大小的线程池");
        for (int i = 0; i <10 ; i++) {
            threadPool1.execute(()->{
                System.out.println(Thread.currentThread().getName()+" ok");
            });
        }
        TimeUnit.SECONDS.sleep(2);
        System.out.println("自动适应大小的线程池");
        for (int i = 0; i <10 ; i++) {
            threadPool2.execute(()->{
                System.out.println(Thread.currentThread().getName()+" ok");
            });
        }
    }
}

结果如下,单一的线程池只有一个线程,固定大小的线程池的线程数是固定的,容量自适应的线程池并不意味着循环几次就会创建几个线程。

单一的线程池
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
固定大小的线程池
pool-2-thread-1 ok
pool-2-thread-2 ok
pool-2-thread-3 ok
pool-2-thread-4 ok
pool-2-thread-1 ok
pool-2-thread-1 ok
pool-2-thread-2 ok
pool-2-thread-4 ok
pool-2-thread-5 ok
pool-2-thread-1 ok
自动适应大小的线程池
pool-3-thread-1 ok
pool-3-thread-2 ok
pool-3-thread-3 ok
pool-3-thread-1 ok
pool-3-thread-2 ok
pool-3-thread-4 ok
pool-3-thread-6 ok
pool-3-thread-7 ok
pool-3-thread-5 ok
pool-3-thread-1 ok

二、线程池的七大参数

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
//三大方法的本质都是去调用ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,		//核心线程池大小,最少保持的线程数
                          int maximumPoolSize,	//最大核心线程池大小
                          long keepAliveTime,	//空闲线程存活时间,某个线程在该时间里没有处理任何请求,将会被线程池销毁
                          TimeUnit unit,		//存活时间单位
                          BlockingQueue<Runnable> workQueue,	//阻塞队列
                          ThreadFactory threadFactory,			//线程工厂,创建线程的,一般不用动
                          RejectedExecutionHandler handler) {	//拒绝策略
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
}

三、四种拒绝策略

image-20200719171417532

new ThreadPoolExecutor.AbortPolicy()         //银行满了,还有人进来,不处理,抛出异常
new ThreadPoolExecutor.CallerRunsPolicy()    //哪里来去哪里,一般为主线程main调用了线程池,所以回到主线程main处理
new ThreadPoolExecutor.DiscardPolicy()       //队列满了,丢掉任务
new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的线程去竞争,不会抛出异常

四、手动创建线程池

最大线程数该怎么定义
1、CPU密集型  电脑是几核,就是几,可以保持CPU得效率最高
2、IO密集型   判断程序中十分耗IO的线程,一般设置为它的两倍
int num = Runtime.getRuntime().availableProcessors();
System.out.println("电脑的核数:"+num);
public class ThreadPoolExecutorDemo {
    public static void main(String[] args) {
        //自定义线程池
        //场景模拟,现在银行最大有五个柜台,平时只有两个在工作,这个为核心线程数,候客区有三把椅子,
        // 这个是阻塞队列,如果候客区的三个位置满了,就会开启其他三个不在使用的窗口,如果其他三窗口
        //也满了,同时候客区也满了,这时候还有人进来就会触发拒绝策略,根据不同的拒绝策略处理
        ExecutorService myPool = new ThreadPoolExecutor(
                2,
                5,
                1,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()	//银行满了,还有人进来,不处理,抛出异常
        );
        try {
            //最大承载: 阻塞队列+最大核心线程数
            //当前可承载8个,但是9个不一定会抛出异常,可能之前的线程结束了,第九个线程刚好可以处理
            //线程多了就会出现问题
            for (int i = 1; i <=9 ; i++) {
                myPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            myPool.shutdown();
        }
    }
}

运行结果:

1、第一种拒绝策略
java.util.concurrent.RejectedExecutionException: Task gdut.hzh.pool.ThreadPoolExecutorDemo$$Lambda$1/1831932724@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8]
	at gdut.hzh.pool.ThreadPoolExecutorDemo.main(ThreadPoolExecutorDemo.java:30)
2、第二种拒绝策略
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-4 ok
main ok
pool-1-thread-1 ok
pool-1-thread-2 ok
pool-1-thread-5 ok