多线程可并发处理多个任务,提高计算机资源的利用率和工作效率,但不能来一个任务就开一个线程处理,因为创建、切换、销毁线程的开销不小,如果是处理短小任务的话,很有可能得不偿失,甚至造成系统崩溃。为了解决线程开销和,线程池的概念应运而生。
某一时刻的任务队列和线程池.png
J.U.C包中有三个关于线程池的接口,分别是:
(1)Executor:运行新任务的简单接口,将任务提交和任务执行细节解耦。ThreadPoolExecutor实现了它;
(2)ExecutorService:具备管理执行器和任务生命周期的方法,使提交任务机制更完善;
(3)ScheduledExecutorService:支持Future和定期执行任务;
一、ThreadPoolExecutor的构造方法参数
(1)int corePoolSize:该线程池中核心线程数最大值
(2)int maximumPoolSize:该线程池中线程总数最大值
(3)long keepAliveTime:非核心线程闲置超时时长
(4)TimeUnit unit:keepAliveTime的单位
核心线程是线程池必要维护的线程,如果可执行的任务数<=corePoolSize,那么空闲的核心线程只会被阻塞,等待新任务;当可执行任务数>核心线程数的时候,线程不够用了,在线程数<=maximumPoolSize && 线程状态的情况下,可以生成非核心线程处理任务。一旦处理完任务,过了keepAliveTime时候,非核心线程就会被销毁;
(5)BlockingQueue workQueue:阻塞队列,维护着等待执行的Runnable任务对象。有不同类型:LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue、DelayQueue;
(6)ThreadFactory threadFactory(非必需):创建线程的工厂类,可以指定某些参数配置,批量创建线程;
(7)RejectedExecutionHandler handler(非必需):拒绝处理策略,有如下几种:
1)AbortPolicy:直接抛出异常,这是默认策略
2)CallerRunsPolicy:退回给调用者执行任务
3)DiscardOldestPolicy:丢弃队列中靠最前的任务,并执行当前任务
4)DiscardPolicy:直接丢弃任务
5)实现RejectedExecutionHandler接口的自定义handler
二、ThreadPoolExecutor的execute方法
execute方法负责调度线程,执行任务。有一个入参,类型为Runnable,表示可执行的任务。
execute方法流程图.png
一个任务能被线程成功拿到,如果当前核心线程没满,还可以创建核心线程执行任务;要是核心线程满了,就添加到任务队列等待执行;如果线程池状态不对或者添加到队列失败(可能队列已满),那只好执行拒绝处理策略。
三、ThreadPoolExecutor的addWorker方法
addWorker方法将线程封装成工作线程worker,之后会从任务队列获取任务执行。有两个入参,Runnable类型的为可执行任务,boolean的表示是否为新线程是否要为核心线程。
addWorker方法流程图.png
四、Worker类的runWorker方法
Worker类实现了Runnable接口。到了addWorker方法流程中的最后一步启动线程,就会调用Worker类的run方法,继而调用里面的runWorker方法。
runWorker方法流程图.png
五、Worker类的getTask方法
runWorker方法在任务队列不为空的时候,一直调用getTask方法获取任务队列里的任务。
getTask方法流程图.png
当线程池状态不对、任务队列为空、设置了超时时间且在规定时间内,拿不到任务或者总线程已满的时候,就会获取不到任务。
六、ExecutorService和线程池状态
上述方法中一直提到的线程池状态,分别有如下几种:
线程池状态转换.png
(1)RUNNING:线程池创建之后即为RUNNING状态,表示可以接收任务;
(2)SHUTDOWN:调用shutdown()之后就不能接收新任务了,但可以处理已经任务队列里、在线程池执行中的任务;
(3)STOP:调用shutdownNow(),不会接收新任务,中断正在执行任务的线程,丢掉在任务队列里的任务;
(4)TIDYING:所有的任务都已经终止了,任务数量为0,接着执行钩子函数terminated()
(5)TERMINATED:线程池彻底终止
思考:在什么时候需要判断线程池状态?在什么时候需要一直循环执行逻辑?
网友评论