- 線程協作
所謂線程協作也就是多線程併發編程中,線程與線程之間完成同一個業務邏輯,期間可能有線程等待、線程通知。
- 線程等待實現方式
Thread線程類的sleep方法,等待指定時間後自動喚醒線程。Object類的wait 方法,線程睡眠後只能通過Object的notify或notifyAll喚醒線程。
notify 方法隨機喚醒多個等待線程中的一個,而notifyAll方法則喚醒所有等待線程。
- wait、notify或notifyAll方法使用注意項
- wait和notify使用的模範代碼邏輯如下
等待通知的代碼邏輯一般爲: synchronized(對象){ while(條件不滿足){ 對象.wait(); } //業務邏輯 } 接收通知的代碼邏輯一般爲: synchronized(對象){ //業務邏輯,改變條件 對象.notify/notifyAll(); }
-
wait和notify方法使用必須添加synchronized鎖,否則報異常 java.lang.IllegalMonitorStateException
-
鎖對象和wait、notify調用的對象必須是同一個對象
-
一個方法中可以同時出現wait和notify或notifyAll,沒有任何影響,只是取決於業務邏輯
注意:wait和notify方法使用都要加鎖,線程調用wait方法後,另一個線程怎麼還會獲取到鎖?
wait方法內部原理,wait方法調用時其實是把當前線程在wait代碼行處進行休眠,同時釋放鎖。
- wait和notifyAll方法案例,實現消費者和生產者模式
一個線程創建橘子到隊列中,隊列滿了通知監視官,同時休眠。另一個線程判斷隊列中有橘子則進行消費,沒有則通知監視官,同時休眠。監視官接到通知後,分別派遣創建線程或消費線程進行創建或消費,從而讓系統保持平衡進行。
創建和消費代碼如下:
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 生產橘子,消費橘子
*/
public class CreConsumerOrange {
//工廠監視員(notify/wait 鎖對象).不管是生產者,還是消費者,做完本職工作都要通知一下監視官
private Object monitorLock;
//創建橘子隨機大小
private static final Random random=new Random();
private static final int queueSize=5;
private static final Queue<Integer> queue=new LinkedBlockingQueue<>();
public CreConsumerOrange(Object monitor){
this.monitorLock=monitor;
}
//生產線程-生產,創建橘子,隊列小於5則創建
public void createOrange(){
while(true) {
synchronized (monitorLock){
//生產
if(queue.size()<queueSize){
while (queue.size()<queueSize) {
int orange = random.nextInt(20);
queue.add(orange);
System.out.println(Thread.currentThread().getName()+"===========create orange "+orange);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//通知monitorLock ,橘子已經創建完成
monitorLock.notifyAll();
}
else{
try {
monitorLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//消費線程-消費
public void consumeOrange(){
while(true) {
synchronized (monitorLock) {
if(queue.size()>0) {
while (queue.size() >= queueSize) {
Integer peek = queue.remove();
System.out.println(Thread.currentThread().getName()+"=======consumeOrage size is " + peek);
}
monitorLock.notifyAll();
}else{
try {
monitorLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//模擬消費者話費時間
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
創建線程進行消費代碼如下:1個線程創建,同時2個線程進行消費
public class NotifyTest {
public static void main(String[] args) {
Object monitor =new Object();//監視官
CreConsumerOrange factory=new CreConsumerOrange(monitor);
//消費者
new Thread(new Runnable() {
@Override
public void run() {
factory.consumeOrange();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
factory.consumeOrange();
}
}).start();
//生產者
new Thread(new Runnable() {
@Override
public void run() {
factory.createOrange();
}
}).start();
System.out.println("===============main is end===================");
}
}
執行結果如下:
===============main is end===================
Thread-2===========create orange 7
Thread-2===========create orange 0
Thread-2===========create orange 11
Thread-2===========create orange 3
Thread-2===========create orange 12
Thread-1=======consumeOrage size is 7
Thread-2===========create orange 14
Thread-0=======consumeOrage size is 0
Thread-2===========create orange 0
Thread-1=======consumeOrage size is 11
Thread-2===========create orange 16
Thread-0=======consumeOrage size is 3
Thread-2===========create orange 19
...
...