セマフォ
セマフォは、共有リソースに同時にアクセスできるスレッドの数を制限するために使用されます。
- セマフォは、現在利用可能なリソースの数を表すカウンターです。
- 2 つのコア操作 P はリソースに適用され、V はリソース解放操作です。
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6; i++) {
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"\t抢占了车位");
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"\t离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
2 抢占了车位
4 抢占了车位
4 离开了车位
6 抢占了车位
6 离开了车位
3 抢占了车位
3 离开了车位
1 抢占了车位
1 离开了车位
5 抢占了车位
2 离开了车位
5 离开了车位
-
取得 (acquire) スレッドが取得操作を呼び出すと、セマフォを正常に取得するか (セマフォから 1 を引いた値)、スレッドがセマフォを解放するかタイムアウトになるまで待機します。
-
release は実際にセマフォの値を 1 増やしてから、待機中のスレッドをウェイクアップします。
-
セマフォは主に 2 つの目的で使用されます。1 つは複数の共有リソースを相互排他的に使用するため、もう 1 つは同時スレッド数を制御するためです。
CountDownLatch(Latch) は減算を行います
スレッドの同期と連携に使用され、すべてのスレッドがカウントダウンを完了するのを待ちます。
- 構築パラメータは待機カウント値を初期化するために使用され、await() はカウントがゼロに戻るのを待機するために使用され、countDown() はカウントを 1 減分するために使用されます。
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch=new CountDownLatch(6);
for (int i = 1; i <=6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t");
countDownLatch.countDown();
},i+"").start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"\t班长关门走人,main线程是班长");
}
}
1
6
5
4
3
2
main 班长关门走人,main线程是班长
- CountDownLatch には主に 2 つのメソッドがあり、1 つ以上のスレッドが await メソッドを呼び出すと、これらのスレッドはブロックされます。
- 他のスレッドが countDown メソッドを呼び出すと、カウンターは 1 ずつデクリメントされます (countDown メソッドを呼び出すスレッドはブロックされません)。
- カウンタ値が 0 になると、await メソッドによってブロックされていたスレッドが目覚め、実行が継続されます。
CyclicBarrier は加算を行います
CyclicBarrier は文字通り、周期的に使用できるバリアを意味します。しなければならないことは、スレッドのグループがバリア (同期ポイントとも呼ばれます) に到達したときにブロックすることです。最後のスレッドがバリアに到達するまで、バリアがドアを開け、バリアによってブロックされているすべてのスレッドがブロックされ続けます。スレッドは、CyclicBarrier の await() メソッドを通じてバリアに入ります。
public class CyclicBarrierDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
CyclicBarrier cb = new CyclicBarrier(2,()->{
System.out.println("任务完成");
}); // 个数为2时才会继续执行
for (int i = 0; i < 3; i++) {
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"开始运行" +new Date());
try {
cb.await(); // 当个数不足时,等待
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"继续向下运行" +new Date());
});
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"开始运行" +new Date());
try {
TimeUnit.SECONDS.sleep(2);
cb.await(); // 当个数不足时,等待
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"继续向下运行" +new Date());
});
}
}
}
pool-1-thread-2开始运行Sun Jul 02 13:45:56 CST 2023
pool-1-thread-1开始运行Sun Jul 02 13:45:56 CST 2023
任务完成
pool-1-thread-2继续向下运行Sun Jul 02 13:45:58 CST 2023
pool-1-thread-1继续向下运行Sun Jul 02 13:45:58 CST 2023
pool-1-thread-1开始运行Sun Jul 02 13:45:58 CST 2023
pool-1-thread-2开始运行Sun Jul 02 13:45:58 CST 2023
任务完成
pool-1-thread-2继续向下运行Sun Jul 02 13:46:00 CST 2023
pool-1-thread-1继续向下运行Sun Jul 02 13:46:00 CST 2023
pool-1-thread-2开始运行Sun Jul 02 13:46:00 CST 2023
pool-1-thread-1开始运行Sun Jul 02 13:46:00 CST 2023
任务完成
pool-1-thread-1继续向下运行Sun Jul 02 13:46:02 CST 2023
pool-1-thread-2继续向下运行Sun Jul 02 13:46:02 CST 2023
- 両方のスレッドが await を実行すると、await を実行する各スレッドはブロックされ、2 つのスレッドが await を実行すると、ブロックされた 2 つのスレッドは実行を継続します。
- 2 つのスレッドが await を実行すると、出力タスクが完了します。
- CyclicBarrier と CountDownLatch の主な違いは、CyclicBarrier が再利用できることであることに注意してください。
- ここでタスクを実行するスレッドの数は、CyclicBarrier のカウントと一致している必要があります。そうでない場合は、タスク 1 とタスク 2 の両方が下位に実行されると定義しているため、エラーが発生します。スレッドが 3 つ以上ある場合は、 2 つのタスクが実行されました。タスク 1 は下位に実行されます
エクスチェンジャー
Exchanger は、スレッド間のコラボレーションのためのツール クラスです。エクスチェンジャーは、スレッド間でデータを交換するために使用されます。これは、2 つのスレッドが相互にデータを交換できる同期ポイントを提供します。これら 2 つのスレッドは、exchange メソッドを通じてデータを交換します。最初のスレッドが最初に交換メソッドを実行すると、2 番目のスレッドも交換を実行するまで待機します。両方のスレッドが同期ポイントに到達すると、2 つのスレッドはデータを交換できるようになります。このスレッドによって生成されたデータを相手に送信します。したがって、Exchanger の使用の焦点は、スレッドのペアが Exchange() メソッドを使用することであり、スレッドのペアが同期ポイントに到達すると、データが交換されます。したがって、このユーティリティ クラスのスレッド オブジェクトはペアになります。
- Exchanger クラスは 2 つのメソッドを提供します。String Exchange(V x): 交換に使用され、交換を開始し、別のスレッドが Exchange を呼び出すのを待ちます。
- 文字列交換(V x、長いタイムアウト、TimeUnit 単位): 交換に使用され、交換を開始し、別のスレッドが Exchange を呼び出すまで待機し、最大待機時間を設定し、待機時間がタイムアウトを超えると待機を停止します。
public class ExchangerDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
executor.execute(new Runnable() {
String data1 = "克拉克森,小拉里南斯";
@Override
public void run() {
nbaTrade(data1, exchanger);
}
});
executor.execute(new Runnable() {
String data1 = "格里芬";
@Override
public void run() {
nbaTrade(data1, exchanger);
}
});
executor.execute(new Runnable() {
String data1 = "哈里斯";
@Override
public void run() {
nbaTrade(data1, exchanger);
}
});
executor.execute(new Runnable() {
String data1 = "以赛亚托马斯,弗莱";
@Override
public void run() {
nbaTrade(data1, exchanger);
}
});
executor.shutdown();
}
private static void nbaTrade(String data1, Exchanger exchanger) {
try {
System.out.println(Thread.currentThread().getName() + "在交易截止之前把 " + data1 + " 交易出去");
Thread.sleep((long) (Math.random() * 1000));
String data2 = (String) exchanger.exchange(data1);
System.out.println(Thread.currentThread().getName() + "交易得到" + data2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
pool-1-thread-1在交易截止之前把 克拉克森,小拉里南斯 交易出去
pool-1-thread-2在交易截止之前把 格里芬 交易出去
pool-1-thread-3在交易截止之前把 哈里斯 交易出去
pool-1-thread-4在交易截止之前把 以赛亚托马斯,弗莱 交易出去
pool-1-thread-4交易得到格里芬
pool-1-thread-2交易得到以赛亚托马斯,弗莱
pool-1-thread-1交易得到哈里斯
pool-1-thread-3交易得到克拉克森,小拉里南斯