Java线程池
Java 线程池
目录
重点关注几个问题:
-
线程池中线程的生命周期
- 线程池启动后,如何初始化工作线程
- 线程数如何变化
- 如何管理空闲的线程
- 提交任务给工作线程、到工作线程完成任务的过程
-
线程池内异常处理?
-
线程池的启动和关闭
ThreadPoolExecutor
概览
-
线程池解决了什么问题:
- 执行大量异步任务时性能比较好,通过复用线程池中的线程,来避免创建和销毁线程的开销。
- 提供管理和限制资源的手段,比如限制线程个数或者动态新增线程。
-
基于生产者消费者模型
-
AtomicIntger类型变量ctl表示线程池状态和线程数,最高3位为是状态,低29位是线程个数。
-
消费者线程封装在内部类Worker中,使用HashSet类型的变量workers保存
Worker
-
Worker类实现了Runnable和AQS,run()方法调用了ThreadPoolExecutor的runWorker方法。
/** * Creates with given first task and thread from ThreadFactory. * @param firstTask the first task (null if none) */ Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; //初始化的时候会将this引用传入工厂方法 this.thread = getThreadFactory().newThread(this); } /** Delegates main run loop to outer runWorker. */ public void run() { runWorker(this); }
重要的方法
execute
- execute(Runnable command)方法,负责提交任务到阻塞队列、并在有必要的情况下添加核心线程或者普通线程。
- 在线程数小于核心线程数时,添加核心线程addWorder(Runnable firstTask, boolean core);添加成功则返回。
- 提交任务到阻塞队列、如果提交失败则新增线程、新增线程失败则执行拒绝策略
- submit()是用FutureTask封装了Runnable和Callable,然后FutureTask实现了RunnableFuture,所以能够提交给execute()方法
- 而FutureTask会通过Executors.RunnableAdapter这个适配器类包装Runnable成为Callable,在call()方法调用run()方法,run()方法执行结束后,返回传入的result。
Worker相关
- addWorker()方法,负责增加线程数,将新的Worker添加到workers
- 通过CAS增加线程数,直到增加成功或者检测到线程数达到最大线程数而返回false
- 增加线程成功后,获取全局锁this.mainLock后线程安全地创建Worker实例,并且调用w.thread.start()启动Worker内部封装的线程开始执行任务。
- runWorker()方法,在循环里,获取初始任务或者通过阻塞获取任务,获取任务后在Worker内部锁的保护下执行任务。
- 若初始任务不为null,则执行;否则,getTask()从阻塞队列中阻塞式获取任务,若返回任务为null则退出循环。
- w.lock()在任务执行期间加锁,是为了避免在任务运行期间,其他线程调用shutdown()后正在执行任务的线程被中断(shutdown()只会中断当前被阻塞挂起的线程)
关闭线程池
- shutdown()方法,被调用后,线程池不会再接受新的任务、但是队列里面的任务还是会完成。
- 设置线程池的状态为SHUTDOWN
- 设置空闲线程的中断标志。
- 设置中断标志前会尝试获取Worker的内部锁,因此不会中断正在执行任务的线程。
- tryTerminate()方法,在满足条件的情况下,设置状态为TERMINATED并唤醒阻塞在termination条件队列的全部线程
- SHUTDOWN、无工作线程、工作队列为空
- STOP、无工作线程
- shutdownNow()方法,线程池不会再接受新的任务,中断正在执行的任务,返回被丢弃的工作任务队列。
- 直接设置线程池的状态为STOP
- 中断所有线程。
- 获取工作队列中的任务并返回。
- awaitTermination(long timeout, TimeUnit unit),阻塞调用线程直到线程池状态变为TERMINATED。
- 状态变为TERMINATED的2种方式:
- shutdown()方法,返回前调用tryTerminate()尝试设置状态为TERMINATED
- runWorker()方法,当工作线程运行结束后,会调用processWorkerExit()方法,也会调用tryTerminate()方法尝试设置状态为TERMINATED
- 状态变为TERMINATED的2种方式:
ScheduledThreadPoolExecutor
- 可以在指定延迟时间、定时进行任务调度执行的线程池
run()流程
- 判断是否重复执行的任务
- 否,则直接调用run()方法。
- 是,则:
- 调用FutureTask的runAndReset()方法
- setNextRunTime()设置任务下次运行的时间
- 调用reExecutePeriodic()方法,将任务重新放入——延迟工作队列。
DelayedQueue的take()原理
基于堆实现
- 先获取锁,进入循环,然后获取堆顶的任务。
- 如果堆顶任务为空,则在条件队列上等待。
- 获取任务成功,则判断任务是否到了执行时间。
- 如果到了,直接出队。
- 如果时间没到,判断自己是否leader线程。
- 不是的话,则在条件队列上等待。
- 是leader线程,则调用awaitNanos()超时自动唤醒。
- 唤醒后,将leader线程设置为空。
- 进入下一次循环。
- 退出循环后,用signal()唤醒等待队列中一个等待的线程,释放锁。
不准不开心。