スレッド間の通信を実装するいくつかの方法
1.従来のスレッド通信の同期+待機+通知
Objectクラスのwait()、notify()、notifyAll()メソッドは、同期モニターオブジェクトによって呼び出される必要があります。
a)同期メソッド。このクラスのデフォルトインスタンス(this)は同期モニターであり、同期メソッドで直接呼び出すことができます。
b)同期コードブロック。同期モニターは、同期後の括弧内のオブジェクトであるため、これらの3つのメソッドは、このオブジェクトを使用して呼び出す必要があります。
次に、Conditionを使用してスレッド通信ロック+条件+待機+シグナルを制御します
Lockは同期メソッドまたは同期コードブロックを置き換え、Conditionは同期モニターの機能を置き換えます。
プライベート最終ロックロック= newReentrantLock();
プライベート最終条件con = lock.newCondition();
lock.lock(); con.await(); con.signalAll(); 施錠開錠():
3、ブロッキングキュー(BlockingQueue)を使用してスレッド通信を制御する
BlockingQueueインターフェースは、主にスレッド同期のためのツールとして使用されます。プロデューサーが要素をBlockingQueueに入れようとすると、キューがいっぱいの場合はスレッドがブロックされ、コンシューマーが要素をBlockingQueueに入れようとすると、キューが空の場合はスレッドがブロックされます。
スレッド間通信のいくつかの実装
まず、スレッド間通信には共有メモリとメッセージパッシングの2つのモデルがあることを理解する必要があります。インタビューでよくある質問を分析してみましょう:
1
トピック:AとBの2つのスレッドがあります。Aスレッドは、要素「abc」文字列をコレクションに順番に追加し、5番目に追加すると合計10回追加します、BスレッドがAスレッドの通知を受信して、Bスレッドが関連するビジネスオペレーションを実行できることを願っています。
方法1:volatileキーワード
を使用してvolatileキーワードに基づくスレッド間の相互通信を実現することは、共有メモリを使用するという考え方です。つまり、複数のスレッドが変数を同時に監視することになります。この変数が変化すると、スレッドは対応する変数を認識して実行できますビジネス。これは、達成する最も簡単な方法でもあります
public class TestSync {
// 定义一个共享变量来实现通信,它需要是volatile修饰,否则线程不能及时感知
static volatile boolean notice = false;
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
notice = true;
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
if (notice) {
System.out.println("线程B收到通知,开始执行自己的业务...");
break;
}
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
実行結果は次のとおりです。
方法2:
Objectクラスのwait()メソッドとnotify()メソッドを使用します。ご存じのとおり、Objectクラスはスレッド間通信のためのメソッドを提供します:wait()、notify()、notifyaAl()、これらはマルチスレッド通信の基礎です。実装のアイデアは当然スレッド間通信です。
注:待機と通知は同期して使用する必要があります。待機メソッドはロックを解放し、通知メソッドはロックを解放しません。
public class TestSync {
public static void main(String[] args) {
// 定义一个锁对象
Object lock = new Object();
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
synchronized (lock) {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
lock.notify();// 唤醒B线程
}
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
synchronized (lock) {
if (list.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B收到通知,开始执行自己的业务...");
}
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
結果は
印刷結果のスクリーンショットから、スレッドAがnotify()ウェイクアップ通知を発行した後、スレッドBは独自のスレッドのビジネスを実行した後にのみ実行を開始することがわかります。これは、notify()メソッドがロックを解放せず、待機( )ロックを解除する方法。
方法3:JUCツールクラスCountDownLatch
jdk1.5 を使用した後、多くの並行プログラミング関連のツールクラスがjava.util.concurrentパッケージの下に提供され、並行プログラミングコードの記述を簡素化します。 、スレッド間のシェア変数の状態を維持することと同等
public class TestSync {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(1);
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
countDownLatch.countDown();
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
if (list.size() != 5) {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B收到通知,开始执行自己的业务...");
break;
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
実行結果は次のとおりです。
条件付きのReentrantLockを使用する
public class TestSync {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
lock.lock();
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
condition.signal();
}
lock.unlock();
});
// 实现线程B
Thread threadB = new Thread(() -> {
lock.lock();
if (list.size() != 5) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B收到通知,开始执行自己的业务...");
lock.unlock();
});
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.start();
}
}
実行結果は次のとおりです。
明らかに、このメソッドは使用するのがあまり良くなく、コードの記述は複雑で、スレッドBはロックを取得しないため、Aによって起動された直後に実行できません。このメソッドは、オブジェクトの待機()および通知()と同じです。
方法5:スレッド間ブロッキングとウェイクアップのための基本的なLockSupport
LockSupportは、スレッド間ブロッキングとウェイクアップのための非常に柔軟なツールです。スレッドが起動するのを待つか、最初にスレッドをウェイクアップするかを気にせずに使用してください。
public class TestSync {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 实现线程B
final Thread threadB = new Thread(() -> {
if (list.size() != 5) {
LockSupport.park();
}
System.out.println("线程B收到通知,开始执行自己的业务...");
});
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
LockSupport.unpark(threadB);
}
});
threadA.start();
threadB.start();
}
}
運用実績