天天看點

java之wait()、notify()實作非阻塞的生産者和消費者

一、對于wait()和notify()的解釋

void notify()
    Wakes up a single thread that is waiting on this object’s monitor.
    喚醒等待擷取鎖資源的單個線程
 
    void notifyAll()
    Wakes up all threads that are waiting on this object’s monitor.
    喚醒等待擷取鎖資源的所有線程
 
    void wait( )
    Causes the current thread to wait until another thread invokes the notify() method or the notifyAll( ) method for this object.
    釋放出對象的鎖資源,程式會阻塞在這裡      

二、使用wait()、notify()、notifyAll()應該要注意的地方

 1、wait( ),notify( ),notifyAll( )都不屬于Thread類,屬于Object基礎類,每個對象都有wait( ),notify( ),notifyAll( ) 的功能,因為每個對象都有鎖

  2、當需要調用wait( ),notify( ),notifyAll( )的時候,一定都要在synchronized裡面,不然會報 IllegalMonitorStateException 異常,可以這樣了解,在synchronized(object) {}裡面的代碼才能擷取到對象的鎖。

 3、while循環裡而不是if語句下使用wait,這樣,會線上程暫停恢複後都檢查wait的條件,并在條件實際上并未改變的情況下處理喚醒通知

 4、調用obj.wait( )釋放了obj的鎖,程式就會阻塞在這裡,如果後面被notify()或者notifyAll()方法呼喚醒了之後,那麼程式會接着調用obj.wait()後面的程式。

   5、notify()喚醒等待擷取鎖資源的單個線程,notifyAll( )喚醒等待擷取鎖資源的所有線程

 6、當調用obj.notify/notifyAll後,調用線程依舊持有obj鎖,其它線程仍無法獲得obj鎖,直到調用線程退出synchronized塊或者在原有的obj調用wait釋放鎖,其它的線程才能起來

三、使用wait()、notify()、notifyAll()實作非阻塞式的消費者和生産者

package wait;
 
import java.util.PriorityQueue;
 
 
public class WaitAndNofityTest {
 
    public static final int COUNT = 5;
    //優先隊列,消費者在這個隊列裡面消耗資料,生産者在這個裡面生産資料
    public PriorityQueue<Integer> queue = new PriorityQueue<Integer>(COUNT);
 
    public static void main(String[] args) {
        WaitAndNofityTest waitAndNofityTest = new WaitAndNofityTest();
        Cus cus = waitAndNofityTest.new Cus();
        Make make = waitAndNofityTest.new Make();
        make.start();
        cus.start();
    }
 
    //消費者線程類
    class Cus extends Thread {
 
        @Override
        public void run() {
            System.out.println("Cus queue size is:" + queue.size());
            eatData();
        }
 
        public void eatData() {
            while (true) {
                synchronized (queue) {
                    System.out.println("go to eatData method size is:" + queue.size());
                    while (queue.size() == 0) {
                        try {
                            System.out.println("Cus start notify");
                            queue.notify();
                            System.out.println("Cus start wait");
                            queue.wait();
                            System.out.println("Cus wait after");
                        } catch (InterruptedException e) {
                            queue.notify();
                        }
                    }
                    queue.poll();
                    System.out.println("Cus eatData after size is:" + queue.size());
                }
            }
        }
    }
 
 
    //生産者線程類
    class Make extends Thread {
 
        @Override
        public void run() {
            System.out.println("Make queue size is:" + queue.size());
            addData();
        }
 
        public void addData() {
            while (true) {
                synchronized (queue) {
                    System.out.println("go to addData method size is:" + queue.size());
                    while (queue.size() == COUNT) {
                        try {
                            System.out.println("Make start notify");
                            queue.notify();
                            System.out.println("Make start wait");
                            queue.wait();
                            System.out.println("Make wait after");
                        } catch (InterruptedException e) {
                            queue.notify();
                        }
                    }
                    queue.offer(5);
                    System.out.println("Cus addData after size is:" + queue.size());
                }
            }
        }
    }
}      

四、部分運作結果

Cus addData after size is:5
go to addData method size is:5
Make start notify
Make start wait
Cus wait after
Cus eatData after size is:4
go to eatData method size is:4
Cus eatData after size is:3
go to eatData method size is:3
Cus eatData after size is:2
go to eatData method size is:2
Cus eatData after size is:1
go to eatData method size is:1
Cus eatData after size is:0
go to eatData method size is:0
Cus start notify
Cus start wait
Make wait after
Cus addData after size is:1
go to addData method size is:1
Cus addData after size is:2
go to addData method size is:2
Cus addData after size is:3
go to addData method size is:3
Cus addData after size is:4
go to addData method size is:4
Cus addData after size is:5
go to addData method size is:5
Make start notify
Make start wait
Cus wait after
Cus eatData after size is:4
go to eatData method size is:4
Cus eatData after size is:3
go to eatData method size is:3
Cus eatData after size is:2
go to eatData method size is:2
Cus eatData after size is:1
go to eatData method size is:1
Cus eatData after size is:0
go to eatData method size is:0
Cus start notify
Cus start wait
Make wait after
Cus addData after size is:1
go to addData method size is:1
Cus addData after size is:2
go to addData method size is:2
Cus addData after size is:3
go to addData method size is:3
Cus addData after size is:4
go to addData method size is:4
Cus addData after size is:5
go to addData method size is:5
Make start notify
Make start wait
Cus wait after
Cus eatData after size is:4
go to eatData method size is:4
Cus eatData after size is:3
go to eatData method size is:3
Cus eatData after size is:2
go to eatData method size is:2
Cus eatData after size is:1
go to eatData method siz      

如果運作了不了解,你可以自己測試然後分析列印的資料,當然你也可以注釋掉2個類裡面的任意一個notify方法,然後看是什麼效果,是不是和自己分析的結果一樣,同樣你也可以去掉第一類的wait方法試下,看執行notify方法後是synchronized執行完了呼喚其其它線程或則在調用wait方法之後釋放鎖後是否也會呼喚其線程?

五、用ArrayBlockingQueue實作阻塞式的生産者和消費者

package wait;
 
import java.util.concurrent.ArrayBlockingQueue;
 
public class BlockTest {
    private int COUNT = 5;
    private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(COUNT);
     
    public static void main(String[] args)  {
        BlockTest test = new BlockTest();
        Make make = test.new Make();
        Cus cus = test.new Cus();
        make.start();
        cus.start();
    }
     
    class Cus extends Thread {
         
        @Override
        public void run() {
            eatData();
        }
         
        private void eatData() {
            while (true) {
                try {
                    queue.take();
                    System.out.println("隊列取一個元素,隊列剩餘"+queue.size() + "元素");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
     
    class Make extends Thread {
         
        @Override
        public void run() {
            addData();
        }
         
        private void addData() {
            while(true){
                try {
                    queue.put(5);
                    System.out.println("隊列插入一個元素,隊列剩餘空間:" + (COUNT-queue.size()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}      

六、部分運作結果

隊列取一個元素,隊列剩餘4元素
隊列取一個元素,隊列剩餘3元素
隊列取一個元素,隊列剩餘2元素
隊列取一個元素,隊列剩餘1元素
隊列取一個元素,隊列剩餘0元素
隊列插入一個元素,隊列剩餘空間:4
隊列插入一個元素,隊列剩餘空間:3
隊列插入一個元素,隊列剩餘空間:2
隊列插入一個元素,隊列剩餘空間:1
隊列插入一個元素,隊列剩餘空間:0
隊列取一個元素,隊列剩餘4元素
隊列取一個元素,隊列剩餘3元素
隊列取一個元素,隊列剩餘2元素
隊列取一個元素,隊列剩餘1元素
隊列取一個元素,隊列剩餘0元素
隊列插入一個元素,隊列剩餘空間:4
隊列插入一個元素,隊列剩餘空間:3
隊列插入一個元素,隊列剩餘空間:2
隊列插入一個元素,隊列剩餘空間:1
隊列插入一個元素,隊列剩餘空間:0
隊列取一個元素,隊列剩餘4元素
隊列取一個元素,隊列剩餘3元素
隊列取一個元素,隊列剩餘2元素
隊列取一個元素,隊列剩餘1元素
隊列取一個元素,隊列剩餘0元素
隊列插入一個元素,隊列剩餘空間:4
隊列插入一個元素,隊列剩餘空間:3
隊列插入一個元素,隊列剩餘空間:2
隊列插入一個元素,隊列剩餘空間:1
隊列插入一個元素,隊列剩餘空間:0
隊列取一個元素,隊列剩餘4元素
隊列取一個元素,隊列剩餘3元素
隊列取一個元素,隊列剩餘2元素
隊列取一個元素,隊列剩餘1元素
隊列取一個元素,隊列剩餘0元素
隊列插入一個元素,隊列剩餘空間:4
隊列插入一個元素,隊列剩餘空間:3
隊列插入一個元素,隊列剩餘空間:2
隊列插入一個元素,隊列剩餘空間:1
隊列插入一個元素,隊列剩餘空間:0
隊列取一個元素,隊列剩餘4元素
隊列取一個元素,隊列剩餘3元素
隊列取一個元素,隊列剩餘2元素
隊列取一個元素,隊列剩餘1元素
隊列取一個元素,隊列剩餘0元素
隊列插入一個元素,隊列剩餘空間:4
隊列插入一個元素,隊列剩餘空間:3
隊列插入一個元素,隊列剩餘空間:2
隊列插入一個元素,隊列剩餘空間:1
隊列插入一個元素,隊列剩餘空間:0
隊列取一個元素,隊列剩餘4元素
隊列取一個元素,隊列剩餘3元素
隊列取一個元素,隊列剩餘2元素
隊列取一個元素,隊列剩餘1元素
隊列取一個元素,隊列剩餘0元素
隊列插入一個元素,隊列剩餘空間:4
隊列插入一個元素,隊列剩餘空間:3
隊列插入一個元素,隊列剩餘空間:2
隊列插入一個元素,隊列剩餘空間:1
隊列插入一個元素,隊列剩餘空間:0
隊列取一個元素,隊列剩餘4元素
隊列取一個元素,隊列剩餘3元素
隊列取一個元素,隊列剩餘2元素
隊列取一個元素,隊列剩餘1元素
隊列取一個元素,隊列剩餘0元素
隊列插入一個元素,隊列剩餘空間:4
隊列插入一個元素,隊列剩餘空間:3
隊列插入一個元素,隊列剩餘空間:2
隊列插入一個元素,隊列剩餘空間:1
隊列插入一個元素,隊列剩餘空間:0
隊列取一個元素,隊列剩餘4元素