多線程——4線程間的通信
一、内容安排
- wait和notify方法
- 簡單的生産者消費者
- 面試點
二、文章内容
1. wait和notify方法
當一個對象調用了wait方法那麼它将阻塞目前線程, 直到有另外一個線程調用了目前對象的notify方法,被阻塞的線程才可能被喚醒。
注意: 若要調用這兩個方法必須先獲得對象的鎖,不讓會抛出異常
- 案列: 啟動A、B兩個線程,A線程中調用wait方法在wait之後列印”被喚醒“; 在B線程中先列印”我來喚醒“,然後調用notify方法
public static void main(String[] args) {
//定義公用對象
Object object = new Object();
//線程A
new Thread(()->{
synchronized (object) {
try {
//調用object的wait方法阻塞目前線程
object.wait();
//列印
System.out.println("我被喚醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
//線程B
new Thread(()->{
System.out.println("我要喚醒A線程");
synchronized (object) {
try {
TimeUnit.SECONDS.sleep(3);
object.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
2.簡單的生産者消費者
有了上面提到的wait和notify兩個方法,我們可以利用這兩個方法實作一個簡單(生産者生産一個等待消費者消費再生産)的生産者消費者;
實作思路:
- 開啟兩個線程,一個不斷的生産一個不斷的消費
- 生産者線程生産消息之後喚醒等待中的消費者并讓自己阻塞,等待消費者消費之後讓消費者喚醒自己
- 消費者線程: 消費消息,喚醒生産者線程,然後自己阻塞
具體實作:
public class ProducerConsumer {
static Object LOCK = new Object();
static Boolean isProduced = false;
static Integer i = 0;
public static void main(String[] args) {
// 定義生産者線程
new Thread(() -> {
while (true) {
synchronized (LOCK) {
if (!isProduced) {
System.out.println("生産者生産消息" + ++i);
LOCK.notify();
isProduced = true;
} else {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "producer").start();
// 定義消費者線程
new Thread(() -> {
while (true) {
synchronized (LOCK) {
if (isProduced) {
System.out.println("消費者生産消息" + i);
LOCK.notify();
isProduced = false;
} else {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "consumer").start();
}
}
3.面試考點
- wait和sleep的差別
- 所屬對象不同: wait是Object中的方法, sleep是Thread中的方法
- sleep不會釋放鎖,而wait會釋放鎖
- sleep可以直接使用在任何地方, 但是使用wait方法必須使用在同步帶嗎塊兒中;并且需要使用鎖對象
- sleep自動喚醒, wait需要别人喚醒