java之wait()、notify()實(shí)現(xiàn)非阻塞的生產(chǎn)者和消費(fèi)者

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

        void notify()
        Wakes up a single thread that is waiting on this object’s monitor.
        喚醒等待獲取鎖資源的單個(gè)線程
     
        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.
        釋放出對(duì)象的鎖資源,程序會(huì)阻塞在這里

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

  1、wait( ),notify( ),notifyAll( )都不屬于Thread類(lèi),屬于Object基礎(chǔ)類(lèi),每個(gè)對(duì)象都有wait( ),notify( ),notifyAll( ) 的功能,因?yàn)槊總€(gè)對(duì)象都有鎖

   2、當(dāng)需要調(diào)用wait( ),notify( ),notifyAll( )的時(shí)候,一定都要在synchronized里面,不然會(huì)報(bào) IllegalMonitorStateException 異常,可以這樣理解,在synchronized(object) {}里面的代碼才能獲取到對(duì)象的鎖。


  3、while循環(huán)里而不是if語(yǔ)句下使用wait,這樣,會(huì)在線程暫?;謴?fù)后都檢查wait的條件,并在條件實(shí)際上并未改變的情況下處理喚醒通知

  4、調(diào)用obj.wait( )釋放了obj的鎖,程序就會(huì)阻塞在這里,如果后面被notify()或者notifyAll()方法呼喚醒了之后,那么程序會(huì)接著調(diào)用obj.wait()后面的程序。

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

  6、當(dāng)調(diào)用obj.notify/notifyAll后,調(diào)用線程依舊持有obj鎖,其它線程仍無(wú)法獲得obj鎖,直到調(diào)用線程退出synchronized塊或者在原有的obj調(diào)用wait釋放鎖,其它的線程才能起來(lái)

 
三、使用wait()、notify()、notifyAll()實(shí)現(xiàn)非阻塞式的消費(fèi)者和生產(chǎn)者

    package wait;
     
    import java.util.PriorityQueue;
     
     
    public class WaitAndNofityTest {
     
        public static final int COUNT = 5;
        //優(yōu)先隊(duì)列,消費(fèi)者在這個(gè)隊(duì)列里面消耗數(shù)據(jù),生產(chǎn)者在這個(gè)里面生產(chǎn)數(shù)據(jù)
        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();
        }
     
        //消費(fèi)者線程類(lèi)
        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());
                    }
                }
            }
        }
     
     
        //生產(chǎn)者線程類(lèi)
        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());
                    }
                }
            }
        }
    }

 
四、部分運(yùn)行結(jié)果

    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

如果運(yùn)行了不理解,你可以自己測(cè)試然后分析打印的數(shù)據(jù),當(dāng)然你也可以注釋掉2個(gè)類(lèi)里面的任意一個(gè)notify方法,然后看是什么效果,是不是和自己分析的結(jié)果一樣,同樣你也可以去掉第一類(lèi)的wait方法試下,看執(zhí)行notify方法后是synchronized執(zhí)行完了呼喚其其它線程或則在調(diào)用wait方法之后釋放鎖后是否也會(huì)呼喚其線程?

 
五、用ArrayBlockingQueue實(shí)現(xiàn)阻塞式的生產(chǎn)者和消費(fèi)者

官方API

    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("隊(duì)列取一個(gè)元素,隊(duì)列剩余"+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("隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:" + (COUNT-queue.size()));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

 
六、部分運(yùn)行結(jié)果

    隊(duì)列取一個(gè)元素,隊(duì)列剩余4元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余3元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余2元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余1元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余0元素
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:4
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:3
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:2
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:1
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:0
    隊(duì)列取一個(gè)元素,隊(duì)列剩余4元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余3元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余2元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余1元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余0元素
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:4
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:3
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:2
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:1
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:0
    隊(duì)列取一個(gè)元素,隊(duì)列剩余4元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余3元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余2元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余1元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余0元素
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:4
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:3
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:2
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:1
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:0
    隊(duì)列取一個(gè)元素,隊(duì)列剩余4元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余3元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余2元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余1元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余0元素
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:4
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:3
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:2
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:1
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:0
    隊(duì)列取一個(gè)元素,隊(duì)列剩余4元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余3元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余2元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余1元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余0元素
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:4
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:3
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:2
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:1
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:0
    隊(duì)列取一個(gè)元素,隊(duì)列剩余4元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余3元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余2元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余1元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余0元素
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:4
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:3
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:2
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:1
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:0
    隊(duì)列取一個(gè)元素,隊(duì)列剩余4元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余3元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余2元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余1元素
    隊(duì)列取一個(gè)元素,隊(duì)列剩余0元素
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:4
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:3
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:2
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:1
    隊(duì)列插入一個(gè)元素,隊(duì)列剩余空間:0
    隊(duì)列取一個(gè)元素,隊(duì)列剩余4元素





作者:chen.yu
深信服三年半工作經(jīng)驗(yàn),目前就職游戲廠商,希望能和大家交流和學(xué)習(xí),
微信公眾號(hào):編程入門(mén)到禿頭 或掃描下面二維碼
零基礎(chǔ)入門(mén)進(jìn)階人工智能(鏈接)