コアスレッドプールエグゼキュータ
Javaスレッドは、作業単位が、また、実施メカニズムの両方です。開始からJDK5、別の仕事と執行メカニズムのユニット。機構エグゼキュータによって行わRunnableを、呼び出し可能、などの作業単位。
エグゼキュータフレーム構造とプロセス
それは、次の3つの部分から構成されます。
- タスク:Runnableを、呼び出し可能インターフェース
- ミッション:エグゼキュータ、エグゼキュータと継承ExecutorServiceの
- 非同期計算結果:未来のインタフェース、今後のクラスのインターフェースをFutureTask
プロセスのさまざまな部分の実装:
- メインスレッドは、タスクを作成する(またはRunnableをタスク・オブジェクト呼び出し可能インタフェースを実装)します。執行ツールはRunnableオブジェクトCallableオブジェクトとしてパッケージ化することができます。
- ExecutorSerivceの実行に直接のRunnableオブジェクト、またはあなたがExecutorServiceの実行に提出したオブジェクトのRunnableまたはCallableオブジェクトを置くことができます。
- 提出()今後のインタフェースを実装するオブジェクトを返します。メインスレッドは、タスク実行の完了を待つFutureTask.get()メソッドを実行することができます。メインスレッドはまたFutureTask.cancel()メソッドによって実行されるタスクを取り消すことができます。
エグゼキュータのフレーム部材
執行は、特殊な用途ThreadPoolExecutor次の4つのカテゴリを作成できます。
- ThreadPoolExecutor
- FixedThreadPool、スレッドプール内のスレッドの固定数は、冗長なタスクは、無制限のブロッキングキューを追加します
- SingleThreadPool、単一のスレッドプールのスレッドは、冗長なタスクは、無制限のブロッキングキューを追加されます
- CachedThreadPool、スレッドプールの上限はSynchronousQueueキューを使用することは、ありません
執行は、2つのクラスのScheduledThreadPoolExecutorの特別なアプリケーションを作成することができます。
- ScheduledThreadPoolExecutor
- SingleThreadScheduledExecutor
次の2つのセクションでは、分解がエグゼキュータを説明した後に行われます
ThreadPoolExecutor下的特殊应用(任务执行 部分)
FixedThreadPool
FixedThreadPool的创建方法,FixedThreadPool其实就是ThreadPoolExecutors的特殊用法:
// 只能通过Executors.newFixedThreadPool()创建
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
主要需要注意的是LinkedBlockingQueue
这个无界阻塞队列,该阻塞队列会造成以下几个影响:
- 执行中的线程数达到corePoolSize,新任务会在无界队列中等待,所以线程数不会超过corePoolSize
- 因为不会创建更多的线程,所以maximumPoolSize和keepAliveTime参数就没什么作用了
- 因为是无界队列,所以也不需要用到RejectPolicyHandler
SingleThreadExecutor
该类也是ThreadPoolExecutor的特殊用法
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
相比FixedThreadPool,除一个正在执行任务的线程,其他任务都会一一加入队列中等待执行。
CachedThreadPool
该类比较特殊,是FixedThreadPool的无限版本:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
一手SynchronousQueue
,保证每次offer任务时,需要一个线程poll该任务,否则SynchronousQueue
会阻塞。所以其应用场景就是在任务量多,但是执行时间短的场景下。但是我认为这个方法有点无用,普通的线程池可以完全胜任该工作
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor虽然不是对ThreadPoolExecutor的参数进行调整,但是对其执行流程进行了一个调整。该类主要用来在给定的延时之后执行任务或定期执行任务。ScheduledThreadPoolExecutor和Timer类似,但是更强大。Timer在单线程下执行,如果前一个任务执行时间过长,会影响下一个任务的执行。
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// 继承ThreadPoolExecutor
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
因为是无界队列,所以maximumPoolSize,aliveKeepTime和workQueue三个参数没什么意义(根据流程判断用不到它们)
ScheduledThreadPoolExecutor的执行主要分成两个部分:
- 当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向DelayQueue添加一个实现了RunnableScheduledFuture接口的ScheduledFutureTask
- 线程池会从DelayQueue中获取ScheduledFutureTask,然后执行任务
ScheduledThreadPoolExecutor在ThreadPoolExecutor的基础上做了以下修改:
- 使用DelayQueue作为任务队列
- 获取任务的方式不同
- 执行任务时会做一些修改
接下来就围绕着上面三点不同点,研究ScheduledThreadPoolExecutor
DelayQueue作为任务队列
具体的内容看这篇
DelayQueue封装了一个PriorityQueue,这个PriorityQueue会对队列中的ScheduledFutureTask进行排序。time小的排在前面(时间早的任务先被执行)。如果两个task的taim相同就比较sequenceNumber,sequenceNumber小的排在前面。
获取任务的方式
线程池总从DelayQueue中获取流程:
- 线程1从DelayQueue中获取已到期的ScheduledFutureTask
- 线程1执行该task
- 线程1修改该task的time变量为下一次执行的时间
- 线程1把这个修改time后的task放回DelayQueue中
FutureTask(异步结果 部分)
该类除了Future接口外还实现了Runnable接口。因此可以将FutureTask提交给线程池使用。该任务如果有多个线程尝试去执行,最终只会有一个线程可以执行,其他线程只能等待该任务执行完毕才能继续执行。
FutureTask可以处于以下三种状态:
- 未启动。在run()方法还没执行之前,FutureTask处于未启动状态
- 已启动。在run()方法执行过程中,FutureTask处于已启动状态
- 已完成。在run()方法执行完后正常结束、被取消、抛出异常。
当FutureTask处于未启动或已启动状态,调用get()方法,会让调用线程进入阻塞状态;当FutureTask处于已完成状态,阻塞的get()方法会立即返回
不同状态下调用get/cancel方法,会有不同的影响
总结
无论是ScheduledThreadPoolExecutor还是FixedThreadExecutor等,都是ThreadPoolExecutor的特殊用法,只要把ThreadPoolExecutor搞懂,那么就不需要担心其他的特殊应用。