概略図
ノード
1. Nodeクラスは、スレッド自体とwaitStatusと呼ばれるそのステータスを含む、同期コードにアクセスするためのスレッドのカプセル化です。waitStatusの5つのステータスは次のとおりです。
-
SIGNAL値は-1です。ノードのステータスがSIGNALの場合は、同期ステータスの取得を待機しています。前のノードはヘッドノード、つまり同期ステータスを取得するノードです。
-
CANCELLEDの値は1です。タイムアウトまたは中断のため、ノードはキャンセルされた状態に設定されます。キャンセルされたノードは競合状態のロックに移行しないでください。キャンセルされた状態を変更せず、他の状態に変換することはできません。この状態のノードは、キューから追い出され、GCによってリサイクルされます(ノードのステータス値が1で、キャンセルされたことを示すと、このノードは同期キューから削除されます)。
-
CONDITION値は-2、ノードは待機キューにあり、ノードスレッドは条件で待機しています。他のスレッドが条件で単一の()メソッドを呼び出すと、ノードは待機キューから同期キューに移動します
-
PROPAGATEの値は-3であり、次の共有同期状態の取得が無条件に伝達されることを示します(読み取り/書き込みロックに存在する状態は、将来的にリソースが存在し、複数のスレッドが同時に同期状態になる可能性があることを示します)
-
初期値は0で、現在ロックを取得しているスレッドがないことを示します(初期状態)。
2.Nodeクラスには2つの定数、SHAREDとEXCLUSIVEがあり、それぞれ共有モードと排他モードを表します。
リエントラントロック
ReentrantLockと同期
ReentrantLockはLockの実装クラスであり、相互に排他的な同期ロックです。
- 機能の観点からは、ReentrantLockはSynchronized同期操作よりも洗練されており(通常のオブジェクトのように使用できるため)、Synchronizedにはない次のような高度な機能も実装しています。
- 待機が中断される可能性があります。ロックを保持しているスレッドが長時間ロックを解放しない場合、待機スレッドは待機を中止することを選択できます。これは、実行時間が非常に長い同期ブロックの処理に役立ちます。
- タイムアウトでロックを取得しようとします:指定された時間範囲内でロックを取得し、時間が経過しても返ります。
- ロックを獲得するためにインラインで待機しているスレッドがあるかどうかを判別できます。
- 割り込み要求に応答可能:Synchronizedとは異なり、ロックを取得したスレッドが割り込まれると、割り込みに応答でき、割り込み例外がスローされ、ロックが解除されます。
- 公平なロックを達成できる
- ロック解除の観点から見ると、SynchronizedはJVMレベルで実装されています。いくつかの監視ツールを使用して同期ロックを監視できるだけでなく、コードの実行が異常な場合、JVMは自動的にロックを解除しますが、Lockを使用することはできません。ロックはコードによって実装されますロックが確実に解放されるようにするには、最終的に{}にunLock()を配置する必要があります。
- パフォーマンスの観点から見ると、Synchronizedの初期の実装は比較的非効率的であり、ReentrantLockと比較すると、ほとんどのシナリオのパフォーマンスはかなり異なります。ただし、Java6では多くの改善が行われています。競争が激しい場合、同期パフォーマンスはReetrantLockよりも優れています。競争が激しい場合、同期パフォーマンスは何十倍も低下しますが、ReetrantLockパフォーマンスは正常に維持できます。
リエントラントロックリエントラント
ReentrantLockは、シンクロナイザSyncを内部的にカスタマイズします(SyncはAQSとAOSの両方を実装し、AOSはミューテックスロックを保持する方法を提供します)実際、ロックすると、スレッドオブジェクトはCASアルゴリズムを介して配置されます。二重にリンクされたリストでは、ロックが取得されるたびに、現在維持されているスレッドIDが現在要求されているスレッドIDと同じであり、同じものを再入可能にすることができます。
CountDownLatch
CountDownLatch(カウントダウンタイマー):CountDownLatchは、AQSに基づく同期ツールクラスで、複数のスレッド間の同期を調整するために使用されます。このツールは通常、スレッド待機を制御するために使用され、カウントダウンが終了するまで特定のスレッドを待機させてから、実行を開始できます。
簡単なコード例
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class CloseDoor {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(5);
CloseDoor closeDoor = new CloseDoor();
new Thread(() -> {
closeDoor.sayBye("Lucy");
countDownLatch.countDown();
}).start();
new Thread(() -> {
closeDoor.sayBye("Bob");
countDownLatch.countDown();
}).start();
new Thread(() -> {
closeDoor.sayBye("Lily");
countDownLatch.countDown();
}).start();
new Thread(() -> {
closeDoor.sayBye("Sam");
countDownLatch.countDown();
}).start();
new Thread(() -> {
closeDoor.sayBye("Gates");
countDownLatch.countDown();
}).start();
//班长锁门
try {
System.out.println("同学离开中");
countDownLatch.await();
System.out.println("班长锁门");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void sayBye(String name) {
try {
System.out.println(name + " pack books to bag");
Random random = new Random();
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + " is gone");
}
}
复制代码
演算結果:
サイクリックバリア
簡単なコード例
/**
* 热身运动
*/
public class PlayBasketballDemo {
private static final int THREAD_NUM = 6;
private static final Random random = new Random();
public static void main(String[] args) throws InterruptedException {
String[] names = {"王五", "李四", "冯北", "刘青扬", "刘士凯", "薛贵", "赵钱孙"};
// 使用构造方法:public CyclicBarrier(int parties, Runnable barrierAction)
// 参数parties表示一共有多少线程参与这次“活动”,barrierAction是可选的,用来指定当所有线程都完成这些必须的“任务”之后要干的其他事情
CyclicBarrier barrier = new CyclicBarrier(THREAD_NUM , new Runnable() {
@Override
public void run() {
// 最后写完数据的线程,会执行这个任务
System.out.println("\n人都到齐了,开始热身运动\n");
}
});
// 启动5个线程,写数据
for (int i = 0; i < THREAD_NUM; i++) {
if (i < THREAD_NUM - 1) {
Thread t = new Thread(new MyTask(barrier, names[i]), "线程名" + i);
t.start();
} else {
// 最后一个线程延迟3秒执行
Thread.sleep(3000);
Thread t = new Thread(new MyTask(barrier, names[i]));
t.start();
}
}
}
static class MyTask extends Thread {
private CyclicBarrier barrier;
private String name;
public MyTask(CyclicBarrier barrier, String name) {
this.barrier = barrier;
this.name = name;
}
@Override
public void run() {
int time = random.nextInt(1000);
System.out.println(name + " 从宿舍出发");
try {
Thread.sleep(time);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println(name + " 到达篮球场");
try {
// 用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;
// 等待所有线程都调用过此函数才能继续后续动作
// 只等待2s,那么最后一个线程3秒才执行,则必定会超时
// barrier.await(2000, TimeUnit.MILLISECONDS);
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(name + " 开始热身");
}
}
}
复制代码
演算結果:
CyclicBarrierとCountDownLatchの比較
- CountDownLatchはリセットできません。CyclicBarrierをリセットして再利用できます
- CountDownLatchの基本的な操作の組み合わせはcountDown / awaitです。awaitを呼び出すスレッドは、数が十分である限り、1つのスレッドまたは複数のスレッドでcountDownであるかに関係なく、countDownを十分に待機します。CyclicBarrierの基本的な操作の組み合わせは待機しています。すべてのパートナーが待機すると、タスクが続行され、自動的にリセットされます。
- CountDownLatchの目的は、他のN個のスレッドが特定の条件に達するのをスレッドに待機させてから、それ自体で何かを実行することです(CyclicBarrierの2番目の構築メソッドを通じて、
public CyclicBarrier(intparties,RunnablebarrierAction)
新しいスレッドで処理を実行しても同じ効果が得られます)。CyclicBarrierの目的は、N個のマルチスレッドがすべてのスレッドが特定の状態に達するまで互いに待機し、N個のスレッドがそれぞれのフォローアップを実行し続けることです(CountDownLatchを使用して、特定の状況で同様の効果を実現できます)。
セマフォ
セマフォはAQS共有モードに基づいて実装されています
簡単なコード例
import java.util.concurrent.Semaphore;
/**
* 吃饭问题
* 现在 N 个人;M双筷子
*/
public class EatNoodles {
private static int PEOPLE_NUM = 8; //人数
private static int CHOPSTICKS_NUM = 5; //筷子个数
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(CHOPSTICKS_NUM);
String[] names = {"Lucy", "Lily", "Tom", "Sam", "Gates", "James", "Kates", "Trump", "Zoe", "Mia"};
for (int i = 0; i < PEOPLE_NUM; i++)
new Worker(names[i], semaphore).start();
}
static class Worker extends Thread {
private String name;
private Semaphore semaphore;
public Worker(String name, Semaphore semaphore) {
this.name = name;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(this.name + " 占用一双筷子在吃饭...");
Thread.sleep(2000);
System.out.println(this.name + " 吃完饭释放筷子...");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
复制代码
演算結果: