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 //表示拒绝处理任务时的策略,主要是丢弃任务 );
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); }
d. 线程池容量的动态调整 setCorePoolSize()和setMaximumPoolSize()
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()方法,终止状态
- 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 });