當多個線程操作同一個資源,但是操作的動作不同時,就會需要線程間進行通信。很著名的是生產者消費者的例子。
有一個盤子,只能放一片面包,生產者生產麪包放入盤子,消費者從盤子中取走麪包吃掉。
由簡單開始,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();
}
}
}
運行結果如圖:
由運行結果可以看到。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()是把線程池中的所有等待線程都喚醒。