Java多執行緒中wait 和 notify 方法理解

Cold不叫寇德發表於2019-01-19

簡單介紹

wait()方法是Object類裡的方法;當一個執行緒執行到wait()方法時,它就進入到一個和該物件相關的等待池中,同時失去(釋放)了物件的機鎖(暫時失去機鎖,wait(long timeout)超時時間到後還需要返還物件鎖);其他執行緒可以訪問;wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的執行緒。wiat()必須放在synchronized block中,否則會在program runtime時扔 出“java.lang.IllegalMonitorStateException”異常。
簡單的介紹就到這裡,現在我們用一個例子來深入理解一下
package com.example.demo.test.MultithreadingTest;

public class MyThreadPrinter2 implements Runnable {
    private String name;
    private Object prev;
    private Object self;

    private MyThreadPrinter2(String name, Object prev, Object self) {
        this.name = name;
        this.prev = prev;
        this.self = self;
    }

    @Override
    public void run() {
        int count = 10;
        int x=0;
        while (count > 0) {
            synchronized (prev) {
                synchronized (self) {
                    System.out.print(name);
                    count--;
                    x++;
                    self.notify();
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (prev){
                System.out.print(x);
            }

        }
    }

    public static void main(String[] args) throws Exception {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
        MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
        MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
        MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);

        new Thread(pa).start();
        Thread.sleep(100);
        new Thread(pb).start();
        Thread.sleep(100);
        new Thread(pc).start();
        Thread.sleep(100);
    }
}

輸出結果:
ABC1A1B1C2A2B2C3A3B3C4A4B4C5A5B5C6A6B6C7A7B7C8A8B8C9A9B9C10

這段程式碼為三執行緒間的同步喚醒操作,主要的目的就是ThreadA->ThreadB->ThreadC->ThreadA迴圈執行三個執行緒。
為了控制執行緒執行的順序,那麼就必須要確定喚醒、等待的順序,所以每一個執行緒必須同時持有兩個物件鎖,才能繼續執行。一個物件鎖是prev,就是前一個執行緒所持有的物件鎖。還有一個就是自身物件鎖。主要的思想就是,為了控制執行的順序,必須要先持有prev鎖,也就前一個執行緒要釋放自身物件鎖,再去申請自身物件鎖,兩者兼備時列印,之後首先呼叫self.notify()釋放自身物件鎖,喚醒下一個等待執行緒,再呼叫prev.wait()釋放prev物件鎖,終止當前執行緒,等待迴圈結束後再次被喚醒。prev.wait()之後,加入了一個物件鎖是prev的輸出x,用來展示什麼時候釋放了prev物件鎖。
執行上述程式碼,可以發現先列印出A,再釋放A,然後C鎖,進入ThreadB,列印出B,再釋放B,然後A鎖,進入ThreadC,列印出C,再釋放C,然後B鎖,此時會進入ThreadA,繼續執行開始時wait的執行緒,輸出x,列印出A,再釋放A,然後C鎖……如此迴圈

相關文章