java同時プログラミングシリーズの原理編--JDKにおける通信ツール類Semaphore
3943 ワード
前言
javaマルチスレッド間で通信する場合、JDKは主に以下のような通信ツール類を提供しています。主にSemaphore、CountDownLatch、CyclicBarrier、exchange、Phiterといった通信類があります。各工具類の役割、原理、用法を詳しく紹介します。
Semaphoreの紹介
Semaphoreが翻訳してくるのは信号の意味です。名前の通り、このツール類が提供する機能は複数のスレッドが互いに「信号を送る」ことです。この「信号」は、intタイプのデータであり、スレッドがリソースにアクセスする数を限定するための「リソース」としてもよい。その構造関数は二つあります。一つのパラメータはスレッドアクセスリソースの数を指定します。二つのパラメータの一つは、スレッドアクセスリソースの数を指定し、一つは公平ロックであるかどうかを指定します。アンフェアロックの概念については、記事javaプログラミングシリーズの原理編-synchronizedとロックを参照してください。コンストラクタコードは以下の通りです。
Semaphoreの使用
Semaphoreは主にスレッドアクセスリソースの数を制御するためのシーンです。例を挙げると、合併の条件の下で、3つのスレッドにあるタスクを実行させたいだけです。サンプルコードを見てください。
現在のスレッドは1で、残りのスレッドリソースは1つしかありません。0スレッドが待機中です。現在のスレッドは0で、残りは2つのスレッドリソースが使用できます。0スレッドが待機中です。現在のスレッドは2で、残りは0スレッドのリソースがあります。0スレッドが待機中です。スレッド2は、リソースが現在リリースされているスレッドが3であり、残りは0スレッドのリソースがあり、6スレッドが待機中である。スレッド1は、リソースをリリースしています。現在のスレッドは4です。残りは0スレッドのリソースがあります。5スレッドが待機しています。スレッド0は、リソースが現在リリースされているスレッドが5であり、残りは0スレッドのリソースが使用可能であり、4スレッドが待機中である。スレッド3は、リソースが現在リリースされているスレッドが6であり、残りは0スレッドのリソースが使用可能であり、3スレッドが待機中である。スレッド4は、リソースが現在リリースされているスレッドが7であり、残りは0スレッドのリソースが使用可能であり、2スレッドが待機中である。スレッド5は、リソースが現在リリースされているスレッドは8であり、残りは0スレッドのリソースがあり、1スレッドが待機中である。スレッド8は、リソースが現在リリースされているスレッドが9であり、残りは0スレッドのリソースが使用可能であり、0スレッドが待機中である。スレッド7は、リソーススレッド6をリリースし、リソーススレッド9をリリースしました。
この結果から、最初にこの3つのリソースを奪ったスレッドは1、0、2であり、他のスレッドは待ち行列に入っていることが分かります。その後、スレッドがあるたびにリソースが解放され、待ち行列のスレッドがリソースに奪われます。Semaphoreデフォルトのacquire方法はスレッドをキューに入れ、中断異常をスローします。しかし、割り込みを無視する方法もあります。またはブロック列に入らない方法もあります。
Semaphore内部にはAQSを継承した同期器Syncのメンバー変数があり、tryAcquire Shared方法を書き換えました。この方法では、資源の獲得を試みます。取得に失敗した場合(現在のリソースの数より小さいリソースが欲しい)、負の数が返されます。そして現在のスレッドはAQSの待ち行列に入ります。具体的なコードロジックはJDK 1.8の中でjava.util.co ncurrentが包むSemaphore類を確認してください。
参照リンク
ここで感謝しています。幸いにも各工場の大神さんたちからのソースプロジェクトが深入にJavaマルチスレッドから出てきて、マルチスレッドの知識に対してもっと深い理解ができます。文章の中で、どこか適当でないところや疑問があったら、メッセージを残して、お互いに勉強しましょう。ありがとうございます
javaマルチスレッド間で通信する場合、JDKは主に以下のような通信ツール類を提供しています。主にSemaphore、CountDownLatch、CyclicBarrier、exchange、Phiterといった通信類があります。各工具類の役割、原理、用法を詳しく紹介します。
Semaphoreの紹介
Semaphoreが翻訳してくるのは信号の意味です。名前の通り、このツール類が提供する機能は複数のスレッドが互いに「信号を送る」ことです。この「信号」は、intタイプのデータであり、スレッドがリソースにアクセスする数を限定するための「リソース」としてもよい。その構造関数は二つあります。一つのパラメータはスレッドアクセスリソースの数を指定します。二つのパラメータの一つは、スレッドアクセスリソースの数を指定し、一つは公平ロックであるかどうかを指定します。アンフェアロックの概念については、記事javaプログラミングシリーズの原理編-synchronizedとロックを参照してください。コンストラクタコードは以下の通りです。
//
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
その最も主要な二つの方法はacquire()とrelease()である。acquire()方法はpermitを申請しますが、release方法はpermitをリリースします。もちろん、複数のacquireを申請したり、複数のreleaseをリリースしたりすることもできます。毎回acquireで、permitsは一つ以上減少します。0に減少し、他のスレッドがacquireに来たら、他のスレッドがあるまでこのスレッドをブロックします。Semaphoreの使用
Semaphoreは主にスレッドアクセスリソースの数を制御するためのシーンです。例を挙げると、合併の条件の下で、3つのスレッドにあるタスクを実行させたいだけです。サンプルコードを見てください。
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
new Thread(new MyThread(i,semaphore)).start();
}
}
static class MyThread implements Runnable{
private int id;// ID
private Semaphore semaphore;
public MyThread(int id, Semaphore semaphore){
this.id = id;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
// permit
semaphore.acquire();
//
System.out.println(String.format(" %d, %d , %d 。",
id,semaphore.availablePermits(),semaphore.getQueueLength()));
Random random = new Random();
// ,
Thread.sleep(random.nextInt(1000));
System.out.println(String.format(" %d ",id));
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// ,
semaphore.release();
}
}
}
}
出力結果:現在のスレッドは1で、残りのスレッドリソースは1つしかありません。0スレッドが待機中です。現在のスレッドは0で、残りは2つのスレッドリソースが使用できます。0スレッドが待機中です。現在のスレッドは2で、残りは0スレッドのリソースがあります。0スレッドが待機中です。スレッド2は、リソースが現在リリースされているスレッドが3であり、残りは0スレッドのリソースがあり、6スレッドが待機中である。スレッド1は、リソースをリリースしています。現在のスレッドは4です。残りは0スレッドのリソースがあります。5スレッドが待機しています。スレッド0は、リソースが現在リリースされているスレッドが5であり、残りは0スレッドのリソースが使用可能であり、4スレッドが待機中である。スレッド3は、リソースが現在リリースされているスレッドが6であり、残りは0スレッドのリソースが使用可能であり、3スレッドが待機中である。スレッド4は、リソースが現在リリースされているスレッドが7であり、残りは0スレッドのリソースが使用可能であり、2スレッドが待機中である。スレッド5は、リソースが現在リリースされているスレッドは8であり、残りは0スレッドのリソースがあり、1スレッドが待機中である。スレッド8は、リソースが現在リリースされているスレッドが9であり、残りは0スレッドのリソースが使用可能であり、0スレッドが待機中である。スレッド7は、リソーススレッド6をリリースし、リソーススレッド9をリリースしました。
この結果から、最初にこの3つのリソースを奪ったスレッドは1、0、2であり、他のスレッドは待ち行列に入っていることが分かります。その後、スレッドがあるたびにリソースが解放され、待ち行列のスレッドがリソースに奪われます。Semaphoreデフォルトのacquire方法はスレッドをキューに入れ、中断異常をスローします。しかし、割り込みを無視する方法もあります。またはブロック列に入らない方法もあります。
//
public void acquireUninterruptibly()
public void acquireUninterruptibly(int permits)
// , CAS
public boolean tryAcquire
public boolean tryAcquire(int permits)
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException
public boolean tryAcquire(long timeout, TimeUnit unit)
Semaphoreの原理Semaphore内部にはAQSを継承した同期器Syncのメンバー変数があり、tryAcquire Shared方法を書き換えました。この方法では、資源の獲得を試みます。取得に失敗した場合(現在のリソースの数より小さいリソースが欲しい)、負の数が返されます。そして現在のスレッドはAQSの待ち行列に入ります。具体的なコードロジックはJDK 1.8の中でjava.util.co ncurrentが包むSemaphore類を確認してください。
参照リンク
ここで感謝しています。幸いにも各工場の大神さんたちからのソースプロジェクトが深入にJavaマルチスレッドから出てきて、マルチスレッドの知識に対してもっと深い理解ができます。文章の中で、どこか適当でないところや疑問があったら、メッセージを残して、お互いに勉強しましょう。ありがとうございます