1. 線程間的通信
-
線程間通信
- 生產者+消費者
- 通知等待喚醒機制
-
多線程編程模板
- 判斷 干活 通知
- 判斷需使用while,以防止中斷和虛假喚醒(見java.lang.Object的API)
A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one
synchronized (obj) { while (<condition does not hold>) obj.wait(timeout); ... // Perform action appropriate to condition }線程也可以在沒有通知、中斷或超時的情況下被喚醒,這就是所謂的假喚醒。雖然這種情況在實踐中很少發(fā)生,但應用程序必須通過測試導致線程被喚醒的條件來防止這種情況發(fā)生,如果條件不滿足,則繼續(xù)等待。換句話說,等待應該總是出現(xiàn)在循環(huán)中,就像這個循環(huán)一樣
1.1 synchronized版
-
synchronized實現(xiàn)
- 先2個線程操作資源類,資源中的操作判斷使用if,如線程A和線程B,可以正常運行1 0 1 0 1 0...
- 增加2個線程C和D,模擬虛假喚醒,判斷依舊是if,運行的結果數(shù)字不是全部為1、0
- 原因:在java多線程判斷時,不能用if,程序出事出在了判斷上面,突然有一添加的線程進到if了,突然中斷了交出控制權,沒有進行驗證,而是直接走下去了,加了兩次,甚至多次
- 在4線程中,將資源類的if判斷改為while判斷,while是只要喚醒就要拉回來再判斷一次
package juc.demo; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Description: * 現(xiàn)在兩個線程, * 可以操作初始值為零的一個變量, * 實現(xiàn)一個線程對該變量加1,一個線程對該變量減1, * 交替,來10輪。 * @Package: juc.demo * @ClassName NotifyWaitDemo * @author: wuwb * @date: 2020/10/19 13:30 */ public class NotifyWaitDemo { public static void main(String[] args) { int turn = 1000; //資源類 ShareData data = new ShareData(); new Thread(() -> { for (int i = 0; i < turn; i++) { try { data.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 0; i < turn; i++) { try { data.decrement(); } catch (Exception e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 0; i < turn; i++) { try { data.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 0; i < turn; i++) { try { data.decrement(); } catch (Exception e) { e.printStackTrace(); } } }, "D").start(); } } //資源類 class ShareData{ private int number = 0; public synchronized void increment() throws InterruptedException { //判斷 if換while while (number != 0) { this.wait(); } //干活 number++; System.out.println(Thread.currentThread().getName() + ":" + number); //通知 this.notifyAll(); } public synchronized void decrement() throws InterruptedException { while (number == 0) { this.wait(); } number--; System.out.println(Thread.currentThread().getName() + ":" + number); this.notifyAll(); } }
1.2 JUC版

1608004387960.png
-
Lock 及 Condition
package juc.demo; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Description: * 現(xiàn)在兩個線程, * 可以操作初始值為零的一個變量, * 實現(xiàn)一個線程對該變量加1,一個線程對該變量減1, * 交替,來10輪。 * @Package: juc.demo * @ClassName NotifyWaitDemo * @author: wuwb * @date: 2020/10/19 13:30 */ public class NotifyWaitDemo { public static void main(String[] args) { int turn = 1000; //資源類 ShareData data = new ShareData(); new Thread(() -> { for (int i = 0; i < turn; i++) { try { data.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 0; i < turn; i++) { try { data.decrement(); } catch (Exception e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 0; i < turn; i++) { try { data.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 0; i < turn; i++) { try { data.decrement(); } catch (Exception e) { e.printStackTrace(); } } }, "D").start(); } } //資源類 class ShareData{ private int number = 0; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void increment() { lock.lock(); try { //判斷 while (number != 0) { condition.await();//this.wait(); } //干活 number++; System.out.println(Thread.currentThread().getName() + ":" + number); //通知 condition.signalAll();//this.notifyAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() { lock.lock(); try { while (number == 0) { condition.await(); } number--; System.out.println(Thread.currentThread().getName() + ":" + number); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
1.3 定制化調用通信
- 使用Lock、Condition指定調用順序,指定喚醒哪個線程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Description
* 多線程之間按順序調用,實現(xiàn)A->B->C
* ......來10輪
*/
public class ThreadOrderAccess {
public static void main(String[] args) {
ShareResource resource = new ShareResource();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource.printC();
}
}, "C").start();
}
}
class ShareResource{
/**標志位*/
private int number = 1;
private Lock lock = new ReentrantLock();
/**3把鑰匙*/
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void printA() {
lock.lock();
try {
while (number != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"==>AAAAAAAAAA");
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (number != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"==>BBBBBBBBBB");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (number != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"==>CCCCCCCCCC");
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}