I.はじめに
スレッドプールとは何ですか
スレッドプールは、タスクを処理するマルチスレッド処理のフォームをキューに追加して、スレッドが作成されたときに自動的にこれらの作業を開始されますです。
なぜ、スレッドプールを使用します
同時要求の数多くが、各スレッドの実行時間が非常に短い場合は、頻繁に作成および破棄のスレッドが表示されます。このように、それは非常に頻繁に時間のスレッドを作成し、破壊することができる、システムの効率を低下させる、リソースのオーバーヘッドは、実際の仕事よりも大きいことが必要。
スレッドプールを導入する必要があり、この問題のためです。使用する スレッドプールの利点を 、以下の通りであります:
- リソースの消費を削減 -作成されたスレッドを再利用することで、消費に起因するスレッドの作成と破壊を減らします。
- 応答時間を改善 -タスクが到着したときに、タスクは、スレッドの作成を直ちに実施することが可能になるまで待つ必要がないかもしれません。
- スレッドの管理性を向上させる 無制限の作成は、システムリソースを消費していない場合にのみ、スレッドは、希少資源であるが、また、流通、チューニングおよび監視を統一することができますスレッドプールを使用して、システムの安定性を減らします- 。しかし、スレッドプールの合理的な使用することは、その原則をよく知っている必要があります。
二、エグゼキュータフレームワーク
エグゼキュータフレームワークは、非同期タスク実行戦略呼び出し、スケジューリング、実行およびコントロールのセットに基づいてフレームワークであり、その目的は、「ジョブの送信」メカニズムと「どのようにタスクを実行する」分離のを提供することです。
コアAPIの概要
エグゼキュータのフレームワークのコアAPI次のように:
Executor
- タスクを実行するためのシンプルなインターフェース。ExecutorService
-延びExecutor
インタフェースを。スケーラビリティ:- 支持スレッド戻り値。
- ライフサイクル管理スレッドをサポートしています。
ScheduledExecutorService
-延びExecutorService
インタフェースを。スケーラビリティ:定期的なタスクのサポート。AbstractExecutorService
-ExecutorService
インタフェースのデフォルトの実装。ThreadPoolExecutor
-継承コアクラスの執行フレームワークAbstractExecutorService
クラス。ScheduledThreadPoolExecutor
-ScheduledExecutorService
インタフェース、時限スケジュールスレッドプールのタスクの実装。Executors
-呼び出すことにより、Executors
スレッドプールの静的ファクトリメソッドとリターン作成するExecutorService
オブジェクトを。
エグゼキュータ
Executor
インターフェイスのみ定義 execute
受信するための方法 Runnable
のオブジェクトを。
public interface Executor {
void execute(Runnable command);
}
ExecutorServiceの
ExecutorService
インタフェースは延び Executor
インターフェイス、それが提供する invokeAll
、invokeAny
、shutdown
、submit
および他の方法を。
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
比べ:見やすいその支援に規定された方法 Executor
、インターフェースExecutorService
主拡張インターフェースです。
- 支持スレッドの値を返す
sumbit
-invokeAll
、invokeAny
方法は、着信サポートされているCallable
オブジェクト。 - サポートライフサイクル管理スレッド
shutdown
-shutdownNow
、isShutdown
および他の方法。
ScheduledExecutorService
ScheduledExecutorService
インターフェイスは、拡張 ExecutorService
インターフェイスを。
前の二つのインターフェースの全てをサポートするその能力に加えて、それはまた、タイミングスケジュールスレッドをサポートします。
public interface ScheduledExecutorService extends ExecutorService {
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
その拡張インタフェースには、次の機能を提供します。
schedule
この方法は、指定された遅延の後に行うことができるRunnable
、またはCallable
タスク。scheduleAtFixedRate
方法およびscheduleWithFixedDelay
方法は、通常のタスクを実行する時間間隔を指定しました。
三、ThreadPoolExecutor
java.uitl.concurrent.ThreadPoolExecutor
クラスは、 Executor
コアクラスでフレームワーク。そこで、本論文では、どのようなこのクラスに焦点を当てています。
重要な分野
ThreadPoolExecutor
これは、次の重要なフィールドがあります。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
パラメータ:
ctl
- 動作状態とスレッドプールスレッドプールを制御するために使用されるアクティブなスレッドの数。これは、2つの部分で構成されています。- スレッドプールの動作状態(
runState
) - 有効なスレッドプール内のスレッド数(
workerCount
) - それは見ることができます
ctl
使用してInteger
3つの高保存、保存するタイプをrunState
29低保存しました、workerCount
。COUNT_BITS
、29でCAPACITY
あるマイナス1(29 1)29 1を左に、この定数は表しworkerCount
上限は5億についてです。
- スレッドプールの動作状態(
- 動作状態 - スレッドが5つの動作状態の合計をプール:
RUNNING
- 動作ステータス。新しいタスクを受け入れるには、また、ブロッキングキューのタスクを処理することができます。SHUTDOWN
- オフ。私たちは、新しいタスクを受け入れませんが、ブロッキングキューのタスクを処理することができます。- でスレッドプールでは
RUNNING
状態、呼び出したshutdown
メソッドは、スレッドプールは、状態に入るようになります。 finalize
この方法はまた、実行中に呼び出されるshutdown
状態を入力する方法。
- でスレッドプールでは
STOP
- 停止状態。新しいタスクを受け入れてはいけない、それはタスクキューを処理しません。中断されたスレッドがタスクを処理しています。スレッドプールではでているRUNNING
かSHUTDOWN
の状態は、呼び出すshutdownNow
メソッドが状態を入力するスレッドプールを作成します。TIDYING
- 仕上げ状態。すべてのタスクが終了している場合は、workerCount
(スレッドの有効数)が0になった後、状態を入力するには、スレッドプールは、呼び出しをterminated
入力する方法TERMINATED
の状態を。TERMINATED
- 終了状態。terminated
方法状態に入った後に行います。デフォルトのterminated
メソッドは何もしません。入力しTERMINATED
、以下の条件を:- スレッドプールではありません
RUNNING
状態。 - スレッドプールの状態ではありません
TIDYING
状態またはTERMINATED
状態; - 状態は、スレッドプールの場合
SHUTDOWN
とworkerQueue
空。 workerCount
0;- セットアップ
TIDYING
に成功状態。
- スレッドプールではありません
コンストラクタ
ThreadPoolExecutor
最初の3がすべて第四実装に基づいてされている4つの構成方法があります。第四の方法は、以下の構造です。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
パラメータ:
corePoolSize
- カーネルスレッドの数。新しいタスクときexecute
の方法を提出する際には、スレッドプールには、以下の判定を行います。- 実行中のスレッドの数より少ない場合は
corePoolSize
、プール内の他のスレッドがアイドル状態であっても、タスクを処理するための新しいスレッドを作成します。 - プールまたはそれ以上でのスレッドの数の場合
corePoolSize
と少なくmaximumPoolSize
、場合にのみworkQueue
タスクを処理するための新しいスレッドを作成する際に、完全な。 - セットした場合
corePoolSize
とmaximumPoolSize
同じサイズは、スレッドプール内に作成されて固定されています。新しいジョブの投入がある場合ならば、workQueue
完全な、要求が置かれますないworkQueue
で、から待つ空きスレッドがあるworkQueue
タスクとプロセスを取るには、 - スレッドの数がより多いを実行しているか、または等しい場合は
maximumPoolSize
、その場合にはworkQueue
、完全な、そして使用しhandler
たタスクに対処するために、指定されたポリシーを、 - したがって、タスクはそれが順に判定され、送信され
corePoolSize
=>workQueue
=>maximumPoolSize
。
- 実行中のスレッドの数より少ない場合は
maximumPoolSize
- スレッドの最大数。- キューがいっぱいになると、少ないスレッドの最大数よりも作成されているスレッドの数と、スレッド・プールには、タスクを実行するために、新しいスレッドを再作成します。
- 值得注意的是:如果使用了***的任务队列这个参数就没什么效果。
keepAliveTime
:线程保持活动的时间。- 当线程池中的线程数量大于
corePoolSize
的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime
。 - 所以,如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
- 当线程池中的线程数量大于
unit
-keepAliveTime
的时间单位。有 7 种取值。可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。workQueue
- 等待执行的任务队列。用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。ArrayBlockingQueue
- 有界阻塞队列。- 此队列是基于数组的先进先出队列(FIFO)。
- 此队列创建时必须指定大小。
LinkedBlockingQueue
- ***阻塞队列。- 此队列是基于链表的先进先出队列(FIFO)。
- 如果创建时没有指定此队列大小,则默认为
Integer.MAX_VALUE
。 - 吞吐量通常要高于
ArrayBlockingQueue
。 - 使用
LinkedBlockingQueue
意味着:maximumPoolSize
将不起作用,线程池能创建的最大线程数为corePoolSize
,因为任务等待队列是***队列。 Executors.newFixedThreadPool
使用了这个队列。
SynchronousQueue
- 不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。- 每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
- 吞吐量通常要高于
LinkedBlockingQueue
。 Executors.newCachedThreadPool
使用了这个队列。
PriorityBlockingQueue
- 具有优先级的***阻塞队列。
threadFactory
- 线程工厂。可以通过线程工厂给每个创建出来的线程设置更有意义的名字。handler
- 饱和策略。它是RejectedExecutionHandler
类型的变量。当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。线程池支持以下策略:AbortPolicy
- 丢弃任务并抛出异常。这也是默认策略。DiscardPolicy
- 丢弃任务,但不抛出异常。DiscardOldestPolicy
- 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。CallerRunsPolicy
- 只用调用者所在的线程来运行任务。- 如果以上策略都不能满足需要,也可以通过实现
RejectedExecutionHandler
接口来定制处理策略。如记录日志或持久化不能处理的任务。
execute 方法
默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。
提交任务可以使用 execute
方法,它是 ThreadPoolExecutor
的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
execute
方法工作流程如下:
- 如果
workerCount < corePoolSize
,则创建并启动一个线程来执行新提交的任务; - 如果
workerCount >= corePoolSize
,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中; - 如果
workerCount >= corePoolSize && workerCount < maximumPoolSize
,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务; - 如果
workerCount >= maximumPoolSize
,并且线程池内的阻塞队列已满,则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
其他重要方法
在 ThreadPoolExecutor
类中还有一些重要的方法:
submit
- 类似于execute
,但是针对的是有返回值的线程。submit
方法是在ExecutorService
中声明的方法,在AbstractExecutorService
就已经有了具体的实现。ThreadPoolExecutor
直接复用AbstractExecutorService
的submit
方法。shutdown
- 不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。- 将线程池切换到
SHUTDOWN
状态; - 并调用
interruptIdleWorkers
方法请求中断所有空闲的 worker; - 最后调用
tryTerminate
尝试结束线程池。
- 将线程池切换到
shutdownNow
- 立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。与shutdown
方法类似,不同的地方在于:- 设置状态为
STOP
; - 中断所有工作线程,无论是否是空闲的;
- 取出阻塞队列中没有被执行的任务并返回。
- 设置状态为
isShutdown
- 调用了shutdown
或shutdownNow
方法后,isShutdown
方法就会返回 true。isTerminaed
- 当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed
方法会返回 true。setCorePoolSize
- 设置核心线程数大小。setMaximumPoolSize
- 设置最大线程数大小。getTaskCount
- 线程池已经执行的和未执行的任务总数;getCompletedTaskCount
- 线程池已完成的任务数量,该值小于等于taskCount
;getLargestPoolSize
- 线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了maximumPoolSize
;getPoolSize
- 线程池当前的线程数量;getActiveCount
- 当前线程池中正在执行任务的线程数量。
使用示例
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 500, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 100; i++) {
threadPoolExecutor.execute(new MyThread());
String info = String.format("线程池中线程数目:%s,队列中等待执行的任务数目:%s,已执行玩别的任务数目:%s",
threadPoolExecutor.getPoolSize(),
threadPoolExecutor.getQueue().size(),
threadPoolExecutor.getCompletedTaskCount());
System.out.println(info);
}
threadPoolExecutor.shutdown();
}
static class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
}
}
四、Executors
JDK 的 Executors
类中提供了几种具有代表性的线程池,这些线程池 都是基于 ThreadPoolExecutor
的定制化实现。
在实际使用线程池的场景中,我们往往不是直接使用 ThreadPoolExecutor
,而是使用 JDK 中提供的具有代表性的线程池实例。
newSingleThreadExecutor
创建一个单线程的线程池。
只会创建唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它 。
单工作线程最大的特点是:可保证顺序地执行各个任务。
示例:
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
});
}
executorService.shutdown();
}
}
newFixedThreadPool
创建一个固定大小的线程池。
每次提交一个任务就会新创建一个工作线程,如果工作线程数量达到线程池最大线程数,则将提交的任务存入到阻塞队列中。
FixedThreadPool
是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
示例:
public class FixedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
});
}
executorService.shutdown();
}
}
newCachedThreadPool
创建一个可缓存的线程池。
- スレッドプール内のスレッドの数は、処理タスクが必要以上に長い場合、アイドル状態のスレッドの一部が回復します。
- 長い時間は、スレッドプールにタスクを提出しなかった場合は、ワーカースレッドが指定された時間のためにアイドル状態になっている場合、それは、ある(デフォルトは1分です)、その後、ワーカースレッドは自動的に終了します。終了した場合、しかし、あなたが再作成ワーカースレッドに新しいタスク、スレッドプールを提出した場合。
- このスレッドプールは、スレッドプールのサイズを制限しない、スレッドプールのスレッドの最大サイズは、作成することができるオペレーティングシステム(またはJVM)のサイズに完全に依存します。そのため、使用は
CachedThreadPool
、原因同時に実行するスレッドの数が多い、大きな原因のシステムダウンに、そうでない場合、制御タスクの数に注意を払うようにしてください。
例:
public class CachedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
});
}
executorService.shutdown();
}
}
newScheduleThreadPool
スレッドプールのサイズ制限を作成します。このスレッドプールは、タイミングや、定期的にタスクを実行する必要性をサポートします。
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
schedule();
scheduleAtFixedRate();
}
private static void schedule() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 100; i++) {
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
}, 1, TimeUnit.SECONDS);
}
executorService.shutdown();
}
private static void scheduleAtFixedRate() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 100; i++) {
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行");
}
}, 1, 1, TimeUnit.SECONDS);
}
executorService.shutdown();
}
}