1、爲什麼要處理線程間通信 :
當我們需要多個線程共同協作完成一件任務,並且希望他們有規律的執行,那麼此時就需要線程之間進行通信。爲了能達到此種目的我們需要引入一個機制——等待喚醒機制。
2、 等待喚醒機制
這是多個線程間的一種協作機制。
就是在一個線程進行了規定操作後 ,就進入等待狀態 (wait() ), 等待其他線程執行完他們的指定代碼過後 再將 其喚醒 (notify() );在有多個線程進行等待時, 如果需要 ,可以使用 notifyAll()來喚醒所有的等待線程。
2.1 wait() 與notify() 和notifyAll()
wait():令當前線程掛起並放棄CPU、同步資源並等待
- 在當前線程中調用方法: 對象名.wait()
- 使當前線程進入等待(某對象)狀態,直到另一線程對該對象發出notify (或notifyAll) 爲止。
- 調用方法的必要條件:當前線程必須具有對該對象的監控權(加鎖)
- 調用此方法後,當前線程將釋放對象監控權,然後進入等待
- 在當前線程被notify後,要重新獲得監控權,然後從斷點處繼續代碼的執行。
notify()/notifyAll()
-
在當前線程中調用方法: 對象名.notify() 或者 對象名.notifyAll()
-
功能:喚醒等待該對象監控權的一個或者所有線程。
-
調用方法的必要條件:當前線程必須調用了wait()
2.2 面試題:
編寫一個程序,開啓三個線程,這三個線程的名字分別爲A,B,C 每個線程將自己的名字打印10次,交替打印,如ABCABCABCABCABCABC…
方法1:jdk1.5 前:wait()、notify()
代碼:
//兩個線程交替執行
public class TestABABAB {
public static int flag = 1;
public static void main(String[] args) {
// 1、線程通信,需要wait notify 但是這兩個必須和synchronize共同使用
Object objA = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (objA) {
for (int i = 0; i < 10; i++) {
while (flag != 1) {
try {
objA.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
flag = 2;
objA.notifyAll();
}
}
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (objA) {
for (int i = 0; i < 10; i++) {
while (flag != 2) {
try {
objA.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
flag = 3;
objA.notifyAll();
}
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (objA) {
for (int i = 0; i < 10; i++) {
while (flag != 3) {
try {
objA.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
flag = 1;
objA.notifyAll();
}
}
}
}, "C").start();
}
}
執行結果:
jdk1.5後 lock() 和condition
Condition 接口描述了可能會與鎖有關聯的條件變量。這些變量在用 法上與使Object.wait 訪問的隱式監視器類似,但提供了更強大的 功能。需要特別指出的是,單個 Lock 可能與多個 Condition 對象關 聯。爲了避免兼容性問題,Condition 方法的名稱與對應的 Object 版 本中的不同。
在 Condition 對象中,與 wait、notify 和 notifyAll 方法對應的分別是 await、signal 和 signalAll。
Condition 實例實質上被綁定到一個鎖上。要爲特定 Lock 實例獲得 Condition 實例,請使用其 newCondition() 方法。
代碼:
public class TestABABAB1 {
public static int flag = 1;
public static void main(String[] args) {
// 1、線程通信,需要wait notify 但是這兩個必須和synchronize共同使用
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
if (flag != 2) {
try {
conditionB.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
flag = 3;
conditionC.signal();
} finally {
lock.unlock();
}
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
if (flag != 3) {
try {
conditionC.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
flag = 1;
conditionA.signal();
} finally {
lock.unlock();
}
}
}
}, "C").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
if (flag != 1) {
try {
conditionA.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
flag = 2;
conditionB.signal();
} finally {
lock.unlock();
}
}
}
}, "A").start();
}
}
下面給一個鏈接:https://www.jianshu.com/p/40078ed436b4
下面是這道題目的四種優雅寫法。