當多個線程操作同一個資源,但是操作的動作不同時,就會需要線程間進行通信。很著名的是生産者消費者的例子。
有一個盤子,隻能放一片面包,生産者生産面包放入盤子,消費者從盤子中取走面包吃掉。
由簡單開始,i+1。先看一個生産者、一個消費者。
代碼如下:
public class ProducerConsumerDemo {
public static void main(String[] args){
Resource r = new Resource();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
}
}
class Resource{ //公共資源
private String name;
private int count =1;
private boolean flag = false;
public synchronized void set(String name){
if(flag)
try{this.wait();}catch(Exception e){}
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "...生産者:生産了"+this.name);
flag = true;
notify();
}
public synchronized void out(){
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "...消費者:消費了"+this.name);
flag = false;
notify();
}
}
class Producer implements Runnable{
private Resource res;
Producer(Resource res){
this.res = res;
}
public void run(){
while(true){
res.set("面包");
}
}
}
class Consumer implements Runnable{
private Resource res;
Consumer(Resource res){
this.res = res;
}
public void run(){
while(true){
res.out();
}
}
}
運作結果如圖:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQ3chVEa0V3bT9CX5RXa2Fmcn9CXwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwVMRpmT41ESiZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jN4QDMzADM0EzNxgDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
由運作結果可以看到。Thread-0和Tread-1兩個線程是交替進行,生産者生産商品i,消費者就把商品i消費掉。然後生産者生産商品i+1,消費者再消費商品i+1。
本來我自己想的實作過程是這樣的:對于盤子來說,它有兩種狀态,可以往裡放面包和不可以放面包。生産者來了,如果可放,就生産一個面包,并把盤子置為不可放面包的狀态,如果不可放,就什麼都不操作。消費者來了,如果可放(就代表不能取),就什麼都不操作,如果不可放(代表能取),就取走一個面包,并把盤子置為可放面包狀态。代碼如下:
class Resource{ //公共資源
private String name;
private int count =1;
private boolean flag = false;
public synchronized void set(String name){
if(flag)
{
}
//try{this.wait();}catch(Exception e){}
else{
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "...生産者:生産了"+this.name);
flag = true;
}
//notify();
}
public synchronized void out(){
if(!flag){
}
//try{this.wait();}catch(Exception e){}
else{
System.out.println(Thread.currentThread().getName() + "...消費者:消費了"+this.name);
flag = false;
}
//notify();
}
}
與上面的示例中代碼的差別是沒有使用wait()和notify()方法。一樣能實作效果。看圖:
不用使用wait()和notify()與使用有什麼差別呢?既然它存在,肯定是有它的道理的。猜測它的優點是效率更高。用什麼方法可以驗證一下?嘗試着加了運作時間和列印輸出。如圖:
(1)不用wait()/notify()
(2)用wait()/notify()
count為10000時,不使用wait()和notify()生産9999個面包需要1330ms,而使用wait()和notify()生産9999個面包隻需要406ms。多次執行,每次的結果相差不大。
增加一下數量級,再比比,也很明顯:6704msVS 3208ms。
(1)不用wait()/notify()(2)用wait()/notify()
計時代碼增加到了main方法所在類和公共資源類下,代碼如下:
public class ProducerConsumerDemo {
public static long strateTime;
public static void main(String[] args){
Resource r = new Resource();
strateTime = System.currentTimeMillis();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
}
}
class Resource{ //公共資源
public static long endTime;
private String name;
private int count =1;
private boolean flag = false;
public synchronized void set(String name){
if(flag)
// {
// System.out.println("無效執行");
// }
try{this.wait();}catch(Exception e){}
else{
this.name = name + "--" + count++;
if(count==100000){
endTime= System.currentTimeMillis();
System.out.println("程式運作時間:" + ( endTime - ProducerConsumerDemo.strateTime )+"ms");
}
System.out.println(Thread.currentThread().getName() + "...生産者:生産了"+this.name);
flag = true;
}
notify();
}
public synchronized void out(){
if(!flag)
// {
// System.out.println("無效執行");
// }
try{this.wait();}catch(Exception e){}
else{
System.out.println(Thread.currentThread().getName() + "...消費者:消費了"+this.name);
flag = false;
}
notify();
}
}
對比發現,差不多兩倍的差距,效率是不一樣的。是因為啥呢?
//TODO:
多線程方法彙總:
等待喚醒:wait()、notify()、notifyAll()。
waite()是把線程由運作狀态置為等待狀态,等待線程都存線上程池中。
notify()方法是把等待狀态的線程從線程池中喚醒。通常喚醒線程池中的第一個等待的線程。
notifyAll()是把線程池中的所有等待線程都喚醒。