Javaスレッドプールに関しては、手作業で書くことはあまりないかもしれません。使用する場合は、通常、パッケージ化されたものを直接使用します。具体的な内部実装は単純な理解である可能性があります。まず、Javaスレッドプールを使用すると便利です。もちろん、もう1つ重要なものがあります。役割、ねえ、誰もがそれを知っています。
実際、Javaの一部のソースコードの操作メカニズムを理解することは、すべての人の学習に非常に役立ちます。一方で、ソースコードのコードは非常に合理化され、ロジック間の管理は非常にエレガントです。ソースコードに精通しているので、実際に本番環境にあります。問題が発生している可能性のある特定のノードに接続するのは簡単です。そうしないと、実際に大量のログが取り出され、特に同時実行の問題が発生する場合は、少しチェックして再試行します。それは本当に人々を墜落させる可能性があります。
今日は、スレッドプールについて説明しましょう。浅いものから深いものへと少し進んで、構築方法を投稿し、いくつかの重要な属性を紹介しましょう。スレッドプールの鍵はThreadPoolExecutorです。誰もがこの言葉を覚えようとします。そうでなければ、毎日スレッドプールについて話しますが、それがどのタイプかはわかりません。本当に質問されました。恥ずかしすぎます。工法を見てください
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //存活时间
TimeUnit unit, //存活时间单位
BlockingQueue<Runnable> workQueue, //缓存队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler //拒绝策略
) {
if (corePoolSize < 0 ||maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
スレッドプールの構築方法は、ほぼ最終的にこの構築方法を示しています。この構築方法は、いくつかの属性を制限してから値を割り当てることで、わかりやすくなっています。各属性を個別に見ていきましょう。
- corePoolSize:コアスレッドの数。スレッドプールに関しては、誰もがスレッドプールの概念を持っている必要があります。これは、プールのように複数のスレッドが格納される場所です。必要な場合は、1つのスレッドを取り出して使用します。 。したがって、このプールにはスレッドが必要です。もちろん、スレッドの数が多すぎると、サーバーのパフォーマンスが確実に消費され、多くのリソースが消費されます。したがって、スレッドプールを設計すると、スレッドプールを設定するための戦略が策定されます。corePoolSizeは、この戦略のパラメーターです。その意味は、スレッドプールが常に開くスレッドです。もちろん、0にすることもできます。ただし、そうしないことをお勧めします。スレッドプールが開かれているため、ビジネスボリュームは少なくなく、スレッドの頻繁な作成と破棄もリソースを消費します。
- maximumPoolSize:スレッドの最大数。プールであるため、ストレージの数を制限する必要があります。そうしないと、1,000万を超えるデータを渡す場合、1,000万のスレッドを直接開くことができますか?おかしなことに、サーバーは早くハングアップします〜ここでのスレッドの最大数は制限です。送信するデータの量に関係なく、スレッドが最大スレッド数に達すると、スレッドを開き続けることはできません。 。もちろん、このパラメーターを0に設定することはできません。そうしないと、スレッドプールのポイントは何になりますか?もちろん、maximumPoolSize> corePoolSizeは必須です。
- keepAliveTimeスレッドの存続時間(たとえば、corePoolSize = 2、maximumPoolSize = 3)。この時点では、スレッドプールで実行するタスクがないため、keepAliveTimeに従って追加のスレッドを閉じることができます。
- たとえば、単位時間の単位は、keepAliveTimeが3、単位がTimeUnit.HOURS、つまり3時間後に閉じられ、単位がTimeUnit.SECONDS、つまり3秒です。
- workQueueキャッシュキュー、先ほど言ったように、スレッドを開いたままにしない方がよいので、実行する必要のあるアイテムが何千もある場合はどうなるでしょうか。このとき、実行する必要のあるパラメーターはキャッシュキューにスローされ、スレッドが解放されると、スレッドはキャッシュキューに移動してデータをフェッチします。これは、はるかに使いやすい方法です。
- threadFactoryスレッドファクトリ
- ハンドラーはポリシーを拒否します。これら2つについては後で説明します。
さて、上記はスレッドプールの最も基本的なパラメータですが、スレッドプールの効率的な操作を維持するために、当然、一連の内部メカニズムがあります。最新の本から始めて、corePoolSize、maximumPoolSize、workQueueの3つのパラメーターを見てみましょう。これらの3つのパラメーターは明らかに関連しています。たとえば、スレッドが多い場合、workQueueは明らかに少ないデータを配置できます。データはいつworkQueueに配置され、いつスレッドが開かれますか?等々。
コードをアップロードするだけで、誰もがよりはっきりとコードを見ることができます。
public class ThreadPoolTest {
public static BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);
public static void main(String[] args) throws Exception {
// 创建一个核心线程为2,最大线程为8,缓存队列为10的线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 8, 0, TimeUnit.SECONDS, workQueue);
// 拒绝策略先不说,i最大值写小点
for (int i = 0; i < 18; i++) {
poolExecutor.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("线程名:" + Thread.currentThread().getName());
//睡眠下,如果线程运行太快,直接空出来了,就没法试验了
Thread.sleep(200);
}
});
}
// 关闭下线程池,不能让它一直跑着哈
poolExecutor.shutdown();
}
最大スレッド数8、キャッシュキュー10のスレッドプールを作成し、同時に18のタスクを埋めました。ご覧のとおり、合計8つのスレッドが開始され、それぞれが使用していると言えます。糸。
线程名:pool-1-thread-1
线程名:pool-1-thread-2
线程名:pool-1-thread-3
线程名:pool-1-thread-5
线程名:pool-1-thread-4
线程名:pool-1-thread-6
线程名:pool-1-thread-7
线程名:pool-1-thread-8
线程名:pool-1-thread-5
线程名:pool-1-thread-8
线程名:pool-1-thread-1
线程名:pool-1-thread-7
线程名:pool-1-thread-4
线程名:pool-1-thread-3
线程名:pool-1-thread-6
线程名:pool-1-thread-2
线程名:pool-1-thread-7
线程名:pool-1-thread-1
しかし、タスクを17に変更するとどうなりますか?
for (int i = 0; i < 17; i++) {
poolExecutor.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("线程名:" + Thread.currentThread().getName());
//睡眠下,如果线程运行太快,直接空出来了,就没法试验了
Thread.sleep(200);
}
});
}
7つのスレッドのみが開始されていることがわかります
线程名:pool-1-thread-2
线程名:pool-1-thread-3
线程名:pool-1-thread-6
线程名:pool-1-thread-7
线程名:pool-1-thread-1
线程名:pool-1-thread-5
线程名:pool-1-thread-4
线程名:pool-1-thread-3
线程名:pool-1-thread-2
线程名:pool-1-thread-4
线程名:pool-1-thread-5
线程名:pool-1-thread-1
线程名:pool-1-thread-7
线程名:pool-1-thread-6
线程名:pool-1-thread-2
线程名:pool-1-thread-3
线程名:pool-1-thread-7
コアスレッドが開始されていて、17個のタスクが実行され、7個のスレッドが開かれ、10個のキャッシュキューが作成されている必要があります。パートナーは何かを見つけましたか?元々、8つのスレッドを作成してから9つのデータをキャッシュキューに配置していましたが、現在は7つのスレッドのみが作成され、10のデータがキャッシュキューに配置されています。明らかに、スレッドプールの実行順序または使用順序は最初にcoreSize> workQueue> maximumPoolSizeです。コアスレッドが占有され、workQueueがいっぱいになった場合にのみ、スレッドは再び開かれ、最大数のスレッドが開かれるまでデータを処理します。 。
タスクが多すぎる場合はどうなりますか?たとえば、100個のタスクがあります。もちろん、例外がスローされます。デフォルトの拒否戦略はこれを行うことです。もちろん、独自の拒否戦略を定義することもできます。これについては後で説明します。
今日はこれで終わりです。少し簡単です。誰もがそれを一瞥するだけで、笑わないでください。
犠牲も勝利もありません〜