Executor框架
自JDK5开始,Executor作为并发线程的核心框架,通过执行机制作用,与工作单元Runnable和Callable隔离开来。也就是说,Executor通过调度线程来执行工作单元(即任务)。
Executor框架的主要部件
1、任务:实现了Runnable(无返回结果)或者Callable(返回结果)接口的类。
2、线程池执行器:实现了Executor接口的类,以及实现了继承自Executor接口的ExectuorService接口。该框架内有两个重要实现类ThreadPoolExecutor和ScheduledThreadPoolExecutor(延时或周期执行)。
3、异步执行结果:实现了Future或FutureTask接口的类。FutureTask扩展自Runnable和Future接口,它既是任务也是异步结果,因此该接口既可以通过FutureTask.run()执行,也可以通过Executor.submit(FutureTask实例)来执行。并且执行的结果保存在FutureTask中,之后可以通过FutureTask.get()获取结果。在FutureTask中可以通过构造函数FutureTask(Runnable runnable,T result)和FutureTask(Callable)将Runnable或Callable接口实现类包含在其中。
线程池执行器
Executor包含了两个重要实现类ThreadPoolExecutor和ScheduledThreadPoolExecutor,后一实现类扩展自前一实现类,可以设置延时或周期执行任务。所有线程池执行器均是通过构造
new ThreaPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit, runnableTaskQueue, handler)
实例获取线程池执行器。
构造函数中参数那一如下:
corePoolSize:基本线程即核心线程大小
maximumPoolSize:线程最大数量
keepAliveTime: 线程空闲后存活时间
runnableTaskQueue:任务队列
handler:对于不满足执行条件的任务的处理策略
一、ThreadPoolExecutor
通过工厂类Executors可以生成3中线程池执行器:
1、FixedThreadPool: 基本线程数量等于最大线程数量,并且是固定的,任务队列为LinkedBlockingQueue,因此是无界队列,存活时间和处理策略设置无效。虽然任务队列无界,但是受硬件资源限制,最大为Integer.MAX_VALUE。
2、SingleThreadPool:与FixedThreadPool基本相同,唯一不同之处在于基本线程数量和最大线程数量为1。
3、CacheThreadPool: 构造该线程池执行器实例:
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
由此可知,基本线程数量为0,且使用没有容量的SynchronousQueue最为任务队列,但线程池线程最大数量为无界的,因此CacheThreadPool会不断地创建新线程,新线程空闲60s后被销毁。极端情况下,会因为创建线程过多而耗尽CPU和内存资源。
步骤1)新的任务提交后,SynchronousQueue.offer(Runnable task)。如果有空闲的线程就通过SynchronousQueue.poll()获取任务并加以执行。
步骤2)如果该过程不成功即线程池中没有线程,CachedThreadPool会创建一个新线程执行任务。
步骤3)步骤2执行完毕,线程在此处于空闲状态,线程将继续执行SynchronousQueue.poll(keepAliveTime, TimeUnit.SECONDS),该方法最多让空闲线程等待60s,如60s内获取获取到提交的一个新任务,空闲池将执行新任务;否则空线程将终止。
二、ScheduledThreadPoolExecutor
该执行器将任务ScheduledFutureTask放入任务队列DelayQueue,该队列是无界并且具有优先级的,因为DelayQueue中聚合了优先级队列PriorityQueue。这个优先队列按照任务ScheduledFutureTask的三个参数,执行时间time、添加到执行器的序号sequenceNumber来安排任务的优先级,time小的任务先入列,再按照sequenceNumber排列任务,从而保证了FIFO。队列中的任务只有到了执行时间time后再能出列。
周期执行步骤如下:
1)线程从DelayQueue中获取到期的任务ScheduledFutureTask,执行任务。
2)修改任务的time变量为下次执行的时间,线程将该任务重新放入DelayQueue中。
步骤2是与ThreadPoolExecutro执行任务的不同之处。
不清楚的地方看源代码。