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
ScheduledThreadPoolExecutor
可以在指定延迟时间、定时进行任务调度执行的线程池
run()流程
判断是否重复执行的任务
否,则直接调用run()方法。
是,则:
调用FutureTask的runAndReset()方法
setNextRunTime()设置任务下次运行的时间
调用reExecutePeriodic()方法,将任务重新放入——延迟工作队列。
DelayedQueue的take()原理
基于堆实现
先获取锁,进入循环,然后获取堆顶的任务。
如果堆顶任务为空,则在条件队列上等待。
获取任务成功,则判断任务是否到了执行时间。
如果到了,直接出队。
如果时间没到,判断自己是否leader线程。
不是的话,则在条件队列上等待。
是leader线程,则调用awaitNanos()超时自动唤醒。
唤醒后,将leader线程设置为空。
进入下一次循环。
退出循环后,用signal()唤醒等待队列中一个等待的线程,释放锁。
标签:Markdown,Java,队列,Worker,任务,调用,线程,方法
来源: https://www.cnblogs.com/iltonmi/p/14327388.html