共享內存中的兩個同步方法,及同步方法中wait()方法的調用。html
執行wait後,當前線程處於等待狀態,而且會釋放鎖。 執行notify後,當前線程並不會立刻釋放鎖,而是執行完runnable代碼後纔會釋放鎖。java
Java中有一個同步模型-監視器,負責管理線程對對象中的同步方法的訪問,它的原理是:賦予該對象惟一一把'鑰匙',當多個線程進入對象,只有取得該對象鑰匙的線程才能夠訪問同步方法,其它線程在該對象中等待,直到該線程用wait()方法放棄這把鑰匙,其它等待的線程搶佔該鑰匙,搶佔到鑰匙的線程後纔可得以執行,而沒有取得鑰匙的線程仍被阻塞在該對象中等待。 總而言之,synchonized使得只有一個線程能進入臨界代碼區。bash
因爲wait( )所等待的對象必須先鎖住,所以,它只能用在同步化程序段或者同步化方法內,不然,會拋出異常java.lang.IllegalMonitorStateException.多線程
/**
* 倉庫類,用於管理產品的生產、消費和存儲。
*/
public class Storage<T> {
private int index = 0;
private static final int MAX = 10;//最大容量
private List<T> storages = new ArrayList<T>(MAX);//存儲集合
public synchronized void produce(T item) {
while (index >= MAX) {// 判斷倉庫滿了,則等待。
try {
System.out.println("倉庫滿了,等待中...");
this.wait();
System.out.println("倉庫不滿了,開始生產");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("生產>>" + item.toString());
storages.add(item);
index++; //先添加item,在進行加1操做
notify(); //喚醒在此對象監視器上等待的單個線程,即消費者線程
}
public synchronized T consume() {
while (index <= 0) {// 判斷倉庫滿了,則等待。
try {
System.out.println("倉庫爲空,等待中...");
this.wait();
System.out.println("倉庫不爲空,開始消費");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;//先進行減1操做,再remove
T item = storages.remove(index);
System.out.println("消費>>" + item.toString());
notify();
return item;
}
}
複製代碼
public class Phone {
private int id;// 手機編號
public Phone(int id) {
this.id = id;
}
@Override
public String toString() {
return "手機編號:" + id;
}
}
複製代碼
public class Producer implements Runnable {
private Storage<Phone> storage;
public Producer(Storage<Phone> storage) {
this.storage = storage;
}
public void run() {
for(int i = 0;i<20;i++){
storage.produce(new Phone(i));
try {
Thread.sleep(10);//每隔10毫秒生產一個產品
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
複製代碼
public class Consumer implements Runnable {
private Storage<Phone> storage;
public Consumer(Storage<Phone> storage) {
this.storage = storage;
}
public void run() {
for(int i = 0;i<20;i++){
storage.consume();
try {
Thread.sleep(100);//每隔100毫秒消費一個
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
複製代碼
public class ProducerAndConsumer {
public static void main(String[] args) {
Storage<Phone> storage = new Storage<Phone>();
new Thread(new Producer(storage)).start();
new Thread(new Consumer(storage)).start();
}
}
複製代碼
2018.11.18補充 如上代碼知識模擬的一個生產者一個消費者的場景,並且生產者和消費者分別執行20次。徹底可使用while(true)使得一直生產和消費。 對於一個生產者和一個消費者的場景,可使用if來判斷臨界值。並不須要while(xxx)作處理。 而對於多生產者或多消費則須要使用while處理,同時須要使用notifyAll去喚醒。不然可能出現假死的狀況,即生產者和消費者都處於wait,所以notify不單單能夠喚醒異類,也能夠喚醒同類(即生產者也能夠喚醒另外一個生產者)ide