多線程高級講解四: 多線程之間的通訊 (wait、notify)

我在寫這個例子的時候,也出了很多問題。

今天頭很痛,好長時間沒弄出來,心情異常的煩躁。

 

先說下概念吧:

多線程之前通訊,不要想複雜了。他就是一個生產者 與 消費者的概念。

 

比如說一個生活中的例子(這是基於我的理解自己想的一個例子):

在銀行辦業務,需要排隊。 業務員屬於一個線程,辦業務的人屬於一個線程。

那麼在程序裏就相當於:業務員剛坐下開始上班,就處於一個wait狀態(等待),然後業務員就對外面的人立個牌子,空閒中。這就相當於notify,就是告訴外面的人,我這裏可以辦業務。 然後來銀行存錢的人發現,業務員立了個空閒中的牌子,然後存錢的人就從wait(等待狀態變成了)開始去辦業務的狀態,當這個存錢的人業務辦完了,就告訴櫃檯的業務員我辦好了。那麼這個過程也就是 notify ,然後業務員知道了,準備接待下一位存錢的人,業務員又處於一個wait狀態了。 就這樣循環下去。

所以: wait、notify就是一個是等待,一個是通知的意思。

 

 

如果不用,wait和notify會出現什麼情況呢 ?

比如:一個人在存錢的時候,來了個不講理的人,人家的錢還沒存,剛要存的時候,這個不講理的人就說,先給我存錢。這時候,銀行業務員可能擡頭看見了,也可能沒看見,拿着錢就存了。然後第3個人要取錢,卡給了,密碼輸了,又來了一個人,把第3個人推出去了。說了一句,取他一個億。 這時候是不會就亂套了 ?同樣也適用於業務員,不然,來了一個很熱心的業務員,說你讓開,讓我來給這個美女辦業務,上來也不知道要取錢還是存錢,想着這個美女應該是來開戶的吧。所以業務就辦錯了。

 

所以通過上面的例子:就可以知道,很多時候需要使用到wait 和 notify。而且爲了不讓別人插隊,需要一把鎖,把進去辦業務的人 和 業務員都鎖起來,辦好了之後,再解鎖。

 

 

用代碼實現,沒有用 waitnotify沒加鎖的效果:

public class Person {

    private String username;
    private int age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}





public class WriteRunnableThread implements Runnable {

    private Person person;

    public WriteRunnableThread(Person person) {
        this.person = person;
    }

    public void run() {

        /**
         * 定義一個局部變量。
         * 當這個值爲偶數時,爲Person類賦值: 偶數   0
         * 當這個數爲奇數時,爲Person類賦值: 奇數   1
         */
        int data = 0;

        while (true) {
            if (data == 0) {
                person.setUsername("偶數");
                person.setAge(0);
            } else {
                person.setUsername("奇數");
                person.setAge(1);
            }

            /**
             * 依次改變data的值爲: 偶數、奇數
             */
            data = (data + 1) % 2;
        }
    }

}





public class ReadRunnableThread implements Runnable {

    private Person person;

    public ReadRunnableThread(Person person) {
        this.person = person;
    }

    public void run() {

        while (true) {
            System.out.println(person.getUsername() + "," + person.getAge());
        }
    }
}


public class Test {

    /**
     * 現在要用多線程做一件事情:
     * <p>
     * 創建兩個線程,一個線程向Person類寫數據,一個線程從Person類中讀數據。
     */
    public static void main(String[] args) {

        Person person = new Person();
        WriteRunnableThread writeRunnableThread = new WriteRunnableThread(person);
        ReadRunnableThread readRunnableThread = new ReadRunnableThread(person);

        Thread write = new Thread(writeRunnableThread);
        Thread read = new Thread(readRunnableThread);

        write.start();
        read.start();


        /**
         * 會出現的結果:
         *
         * 奇數,1
         * 奇數,0
         * 偶數,0
         * 偶數,1
         *
         * 有沒有發現,這不是我們想要的結果 ?
         * 而且也會發現,拿到的數據,並不是按順序來的。
         */
    }
}

 

 

然後,再來看,使用了wait  和 notify  並且加了同一把鎖的效果:

public class Person {

    public String username;
    public int age;

    /**
     * true 生產者線程等待,   false 消費者線程等待
     */
    public boolean flag = false;
}




public class WriteThread extends Thread {

    private Person person;

    public WriteThread(Person person) {
        this.person = person;
    }

    @Override
    public void run() {

        /**
         * 定義一個局部變量。
         * 當這個值爲偶數時,爲Person類賦值: 偶數   0
         * 當這個數爲奇數時,爲Person類賦值: 奇數   1
         */
        int data = 0;

       while (true){

            synchronized (person) {

                /**
                 * true: 等待消費
                 */
                if (person.flag) {
                    try {
                        person.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                if (data == 0) {
                    person.username = "偶數";
                    person.age = 0;
                } else {
                    person.username = "奇數";
                    person.age = 1;
                }

                /**
                 * 依次改變data的值爲: 偶數、奇數
                 */
                data = (data + 1) % 2;

                person.flag = false;
                person.notify();
            }
        }

    }
}





public class ReadThread extends Thread {

    public Person person;

    public ReadThread(Person person) {
        this.person = person;
    }

    @Override
    public void run() {

        while (true){

            synchronized (person) {

                if (!person.flag) {
                    try {
                        person.wait();
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println(person.username + "," + person.age);
                person.flag = true;
                person.notify();
            }
        }
    }
}





public class Test {

    /**
     * 現在要用多線程做一件事情:
     * <p>
     * 創建兩個線程,一個線程向Person類寫數據,一個線程從Person類中讀數據。
     * <p>
     * 寫這個例子的時候報了很多錯:
     * 1. notify 沒有在synchronized代碼塊中報錯。
     * 2. 沒有使用 synchronized 也報錯了。
     * 3. 沒有使用對象鎖,也報錯了。
     *
     * 所以:
     * 使用wait 和 notify,一定要在同步代碼塊中,同時存在。
     * 因爲wait 和 notify 在Object內中,所以一定要使用同一個對象鎖。
     */
    public static void main(String[] args) {

        Person person = new Person();

        WriteThread writeThread = new WriteThread(person);
        ReadThread readThread = new ReadThread(person);

        writeThread.start();
        readThread.start();
    }
}

 

上面就是使用 wait  notify   加鎖的代碼實現。

重點:(想了解這個可以去看   多線程高級講解五: 多線程的Lock鎖,多線程同步、多線程併發的概念

wait 、notify只能在synchronized中使用,不能在jdk1.5的Lock鎖中使用。

在Lock鎖中,如果要線程之間通訊,需要使用Condition類中的await 和 signal 。

 

wait 與 sleep的區別是什麼 ?

他們都是做休眠的。

wait必須在synchronized同步代碼塊中使用,只要運行到wait的代碼,下面的代碼就不會執行(處於等待狀態),他會等待其他線程notify的喚醒才能從休眠狀態改爲運行狀態,通過上面的例子,還可以看出wait還可以釋放鎖的資源。

sleep在線程中只等待,不會釋放鎖的資源。

 

本來想把上面這個例子,也實現成 銀行業務員  和  客戶這種關係,但是今天實在不想再看電腦屏幕了。將就着看吧。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章