Java线程池原理

一、引入背景

1. 线程频繁的创建和销毁会消耗大量系统资源

2. 线程上线文切换会消耗大量系统资源

3. 线程数量太多,栈内存会溢出,因为每个线程都有自己的栈

4. 需要一种机制,可以线程复用,执行完一个任务后不销毁,继续执行其他任务

5. 还可以提高线程的可控性

 

二、线程池定义

1. 接口关系

a. Executor接口,Excutor框架把任务的提交和执行进行解耦,只声明了一个方法execute()

b. ExecutorService: 继承Executor接口,添加了一些用来管理线程的方法

c. AbstractExecutorService:抽象类,实现了ExecutorService接口

d. ThreadPoolExecutor类: ExecutorService的默认实现,是线程池中最核心的类

e. 继承关系:ThreadPoolExecutor->AbstractExecutorService->ExecutorService->Executor

 

2. ThreadPoolExecutor类构造方法的源码(重要!!!需要透彻理解)

public ThreadPoolExecutor(

            int corePoolSize, //线程池大小,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;

            int maximumPoolSize, //线程池能创建的最大线程数,超出corePoolSize和workerCount后的补救措施;超过maximumPoolSize进行拒绝

            long keepAliveTime, //表示线程没有任务执行时(即在缓存队列中)最多保持多久时间会终止,当线程数大于corePoolSize才起作用,如果一个线程的空闲时间达到keepAliveTime,则会终止

            TimeUnit unit, //参数keepAliveTime的时间单位

            BlockingQueue<Runnable> workQueue, //一个阻塞队列,用来存储等待执行的任务,此时线程被阻塞,当workQueue已满,并且corePoolSize<workerCount<maximumPoolSize时,创建线程执行任务;当workerCount>maximumPoolSize时,拒绝

ThreadFactory threadFactory, //线程工厂,主要用来创建线程,可以对线程命名

RejectedExecutionHandler handler //表示拒绝处理任务时的策略,主要是丢弃任务 );

 

3. ThreadPoolExecutor类的核心方法:
a. execute(Runnable)没有返回值,也是线程池执行的底层原理
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
}
 
 
b. submit(Callable)有返回值,跟Future相关,底层还是调用execute()方法
 
c. 线程池的关闭 shutdown()和shutdownNow()
shutdown() 不接受新的任务
shutdownNow() 不接受新的任务,并尝试终止正在运行的线程

d. 线程池容量的动态调整 setCorePoolSize()和setMaximumPoolSize()

 
 
三、实现原理(重要!!!需要透彻理解)
1. 线程池状态
private static final int RUNNING    = -1 << COUNT_BITS; //正常运行状态
private static final int SHUTDOWN   =  0 << COUNT_BITS; //shutdown()方法,不再接受新任务
private static final int STOP       =  1 << COUNT_BITS; //shutdownNow()方法,不再接受新任务,并尝试终止正在执行的任务
private static final int TIDYING    =  2 << COUNT_BITS; //所有任务都执行完毕
private static final int TERMINATED =  3 << COUNT_BITS; //terminated()方法,终止状态

 
2. 线程池中的线程初始化
  • prestartCoreThread():初始化一个核心线程;
  • prestartAllCoreThreads():初始化所有核心线程

3. 任务缓存队列及排队策略, 如果超过了corePoolSize,会缓存线程到workQueue

4. 任务拒绝策略,如果超出了maxPoolSize,会拒绝任务并丢弃

 

四、创建线程池:

1. Executors的4个静态方法,底层实现都是ThreadPoolExecutor

newSingleThreadExecutor       创建只有一个线程的线程池

newFixedThreadPool      创建线程数量固定的线程池

newCachedThreadPool          创建可缓存的线程池,线程数量无限制

newScheduledThreadPool  创建周期性执行任务的线程池,线程数量限制

2. 直接创建ThreadPoolExecutor对象

 
五、示例代码
ExecutorService executorService = Executors.newFixedThreadPool(20); 

Executor executor=Executors.newFixedThreadPool(20);
executorService.execute(() -> { // code });

 

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            corePoolSize,
            maximumPoolSize,
            keepAliveTime,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(workQueueCount),
            new ThreadPoolExecutor.DiscardOldestPolicy()
    );
ExecutorService executorService = new ThreadPoolExecutor(
            corePoolSize,
            maximumPoolSize,
            keepAliveTime,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(workQueueCount),
            new ThreadPoolExecutor.DiscardOldestPolicy()
    );
threadPoolExecutor.execute(()->{ // code });

 

 
 六、如何配置线程池
1. IO密集型任务:由于线程并不是一直在运行,都在等待IO,所以尽可能多配置线程,比如CPU个数*2
2. CPU密集型任务:分配较少的线程,比如CPU个数
 
 
 
 
 
 
 
 
 
 
posted @ 2019-09-21 20:58  牧云文仔  阅读(236)  评论(0编辑  收藏  举报