1. 程式人生 > >JAVA多執行緒之wait/notify

JAVA多執行緒之wait/notify

本文主要學習JAVA多執行緒中的 wait()方法 與 notify()/notifyAll()方法的用法。

①wait() 與 notify/notifyAll 方法必須在同步程式碼塊中使用

②wait() 與  notify/notifyAll() 的執行過程

③中斷 呼叫wait()方法進入等待佇列的 執行緒

④notify 通知的順序不能錯

⑤多執行緒中測試某個條件的變化用 if 還是用 while?

①wait() 與 notify/notifyAll 方法必須在同步程式碼塊中使用

wait() 與 notify/notifyAll() 是Object類的方法,在執行兩個方法時,要先獲得鎖。那麼怎麼獲得鎖呢?

在這篇:JAVA多執行緒之Synchronized關鍵字--物件鎖的特點文章中介紹了使用synchronized關鍵字獲得鎖。因此,wait() 與  notify/notifyAll() 經常與synchronized搭配使用,即在synchronized修飾的同步程式碼塊或方法裡面呼叫wait() 與  notify/notifyAll()方法。

②wait() 與  notify/notifyAll() 的執行過程

由於 wait() 與  notify/notifyAll() 是放在同步程式碼塊中的,因此執行緒在執行它們時,肯定是進入了臨界區中的,即該執行緒肯定是獲得了鎖的。

當執行緒執行wait()時,會把當前的鎖釋放,然後讓出CPU,進入等待狀態。

 當執行notify/notifyAll方法時,會喚醒一個處於等待該 物件鎖 的執行緒,然後繼續往下執行,直到執行完退出物件鎖鎖住的區域(synchronized修飾的程式碼塊)後再釋放鎖。

從這裡可以看出,notify/notifyAll()執行後,並不立即釋放鎖,而是要等到執行完臨界區中程式碼後,再釋放。故,在實際程式設計中,我們應該儘量線上程呼叫notify/notifyAll()後,立即退出臨界區。即不要在notify/notifyAll()後面再寫一些耗時的程式碼。示例如下:

 1 public class Service {
 2 
 3     public void testMethod(Object lock) {
4 try { 5 synchronized (lock) { 6 System.out.println("begin wait() ThreadName=" 7 + Thread.currentThread().getName()); 8 lock.wait(); 9 System.out.println(" end wait() ThreadName=" 10 + Thread.currentThread().getName()); 11 } 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 17 public void synNotifyMethod(Object lock) { 18 try { 19 synchronized (lock) { 20 System.out.println("begin notify() ThreadName=" 21 + Thread.currentThread().getName() + " time=" 22 + System.currentTimeMillis()); 23 lock.notify(); 24 Thread.sleep(5000); 25 System.out.println(" end notify() ThreadName=" 26 + Thread.currentThread().getName() + " time=" 27 + System.currentTimeMillis()); 28 } 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 }

在第3行的testMethod()中呼叫 wait(),在第17行的synNotifyMethod()中呼叫notify()

從上面的程式碼可以看出,wait() 與  notify/notifyAll()都是放在同步程式碼塊中才能夠執行的。如果在執行wait() 與  notify/notifyAll() 之前沒有獲得相應的物件鎖,就會丟擲:java.lang.IllegalMonitorStateException異常。

在第8行,當ThreadA執行緒執行lock.wait();這條語句時,釋放獲得的物件鎖lock,並放棄CPU,進入等待佇列。

另一個執行緒執行第23行lock.notify();,會喚醒ThreadA,但是此時它並不立即釋放鎖,接下來它睡眠了5秒鐘(sleep()是不釋放鎖的,事實上sleep()也可以不在同步程式碼塊中呼叫),直到第28行,退出synchronized修飾的臨界區時,才會把鎖釋放。這時,ThreadA就有機會獲得另一個執行緒釋放的鎖,並從等待的地方起(第9行)起開始執行。

接下來是兩個執行緒類,執行緒類ThreadA呼叫testMethod()方法執行lock.wait();時被掛起,另一個執行緒類synNotifyMethodThread呼叫synNotifyMethod()負責喚醒掛起的執行緒。程式碼如下:

 1 public class ThreadA extends Thread {
 2     private Object lock;
 3 
 4     public ThreadA(Object lock) {
 5         super();
 6         this.lock = lock;
 7     }
 8 
 9     @Override
10     public void run() {
11         Service service = new Service();
12         service.testMethod(lock);
13     }
14 }
15 
16 public class SynNotifyMethodThread extends Thread {
17     private Object lock;
18 
19     public SynNotifyMethodThread(Object lock) {
20         super();
21         this.lock = lock;
22     }
23 
24     @Override
25     public void run() {
26         Service service = new Service();
27         service.synNotifyMethod(lock);
28     }
29 }

再接下來是測試類:

 1 public class Test {
 2 
 3     public static void main(String[] args) throws InterruptedException {
 4 
 5         Object lock = new Object();
 6 
 7         ThreadA a = new ThreadA(lock);
 8         a.start();
 9 
10         //NotifyThread notifyThread = new NotifyThread(lock);
11        // notifyThread.start();
12 
13         SynNotifyMethodThread c = new SynNotifyMethodThread(lock);
14         c.start();
15     }
16 }

③中斷 呼叫wait()方法進入等待佇列的 執行緒

示例程式碼如下:

 1 public class Service {
 2 
 3     public void testMethod(Object lock) {
 4         try {
 5             synchronized (lock) {
 6                 System.out.println("begin wait()");
 7                 lock.wait();
 8                 System.out.println("  end wait()");
 9             }
10         } catch (InterruptedException e) {
11             e.printStackTrace();
12             System.out.println("出現異常");
13         }
14     }
15 }
16 
17 public class ThreadA extends Thread {
18 
19     private Object lock;
20 
21     public ThreadA(Object lock) {
22         super();
23         this.lock = lock;
24     }
25 
26     @Override
27     public void run() {
28         Service service = new Service();
29         service.testMethod(lock);
30     }
31 }

注意,在第23行wait()方法是Object類的物件lock呼叫的。而下面的interrupt()方法是ThreadA類的物件呼叫的。在ThreadA裡面,將Object的物件作為引數傳給了testMethod()方法,ThreadA的run()方法去呼叫testMethod(),從而wait()使ThreadA的執行緒暫停了(暫停當前執行wait()的執行緒)。從這裡可以看出一個區別:

Object類中與執行緒有關的方法:

1)notify/notifyAll

2)wait()/wait(long)

java.lang.Thread中與之相關的方法:

1)interrupt()

2)sleep()/sleep(long)

3)join()/suspend()/resume()....

測試類程式碼如下:

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4 
 5         try {
 6             Object lock = new Object();
 7 
 8             ThreadA a = new ThreadA(lock);
 9             a.start();
10 
11             Thread.sleep(5000);
12 
13             a.interrupt();
14         } catch (InterruptedException e) {
15             e.printStackTrace();
16         }
17     }
18 }

當執行第13行的interrupt()時,處於wait中的執行緒“立即”被喚醒(一般是立即響應中斷請求),並丟擲異常。此時,執行緒也就結束了。

④notify 通知的順序不能錯

假設線上程A中執行wait(),線上程B中執行notify()。但如果執行緒B先執行了notify()然後結束了,執行緒A才去執行wait(),那此時,執行緒A將無法被正常喚醒了(還可以通過③中提到的interrupt()方法以丟擲異常的方式喚醒^~^)。

這篇文章: JAVA多執行緒之執行緒間的通訊方式中的第③點提到了notify通知順序出錯會導致 呼叫wait()進入等待佇列的執行緒再也無法被喚醒了。

⑤多執行緒中測試某個條件的變化用 if 還是用 while?

以前一直不明白 當線上程的run()方法中需要測試某個條件時,為什麼用while,而不用if???直到看到了這個簡單的例子,終於明白了。。。。

這個例子是這樣的:

有兩個執行緒從List中刪除資料,而只有一個執行緒向List中新增資料。初始時,List為空,只有往List中添加了資料之後,才能刪除List中的資料。新增資料的執行緒向List新增完資料後,呼叫notifyAll(),喚醒了兩個刪除執行緒,但是它只添加了一個數據,而現在有兩個喚醒的刪除執行緒,這時怎麼辦??

如果用 if 測試List中的資料的個數,則會出現IndexOutofBoundException,越界異常。原因是,List中只有一個數據,第一個刪除執行緒把資料刪除後,第二個執行緒再去執行刪除操作時,刪除失敗,從而丟擲 IndexOutofBoundException。

但是如果用while 測試List中資料的個數,則不會出現越界異常!!!神奇。

當wait等待的條件發生變化時,會造成程式的邏輯混亂---即,List中沒有資料了,再還是有執行緒去執行刪除資料的操作。因此,需要用while迴圈來判斷條件的變化,而不是用if。

示例如下:Add類,負責新增資料:

public class Add {

    private String lock;
    public Add(String lock) {
        super();
        this.lock = lock;
    }

    public void add() {
        synchronized (lock) {
            ValueObject.list.add("anyString");
            lock.notifyAll();
        }
    }
}

public class ThreadAdd extends Thread {

    private Add p;

    public ThreadAdd(Add p) {
        super();
        this.p = p;
    }

    @Override
    public void run() {
        p.add();
    }
}

Subtract類,負責刪除資料----先要進行條件判斷,然後執行wait(),這意味著:wait等待的條件可能發生變化!!!

public class Subtract {

    private String lock;

    public Subtract(String lock) {
        super();
        this.lock = lock;
    }

    public void subtract() {
        try {
            synchronized (lock) {
                if(ValueObject.list.size() == 0) {//將這裡的if改成while即可保證不出現越界異常!!!!
                    System.out.println("wait begin ThreadName="
                            + Thread.currentThread().getName());
                    lock.wait();
                    System.out.println("wait   end ThreadName="
                            + Thread.currentThread().getName());
                }
                ValueObject.list.remove(0);
                System.out.println("list size=" + ValueObject.list.size());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadSubtract extends Thread {

    private Subtract r;

    public ThreadSubtract(Subtract r) {
        super();
        this.r = r;
    }

    @Override
    public void run() {
        r.subtract();
    }
}

封裝的List佇列:

public class ValueObject {

    public static List list = new ArrayList();

}

測試類:

public class Run {

    public static void main(String[] args) throws InterruptedException {

        String lock = new String("");

        Add add = new Add(lock);
        Subtract subtract = new Subtract(lock);

        ThreadSubtract subtract1Thread = new ThreadSubtract(subtract);
        subtract1Thread.setName("subtract1Thread");
        subtract1Thread.start();

        ThreadSubtract subtract2Thread = new ThreadSubtract(subtract);
        subtract2Thread.setName("subtract2Thread");
        subtract2Thread.start();

        Thread.sleep(1000);

        ThreadAdd addThread = new ThreadAdd(add);
        addThread.setName("addThread");
        addThread.start();

    }
}

參考:《JAVA多執行緒核心技術》

相關推薦

JAVA執行wait/notify

本文主要學習JAVA多執行緒中的 wait()方法 與 notify()/notifyAll()方法的用法。 ①wait() 與 notify/notifyAll 方法必須在同步程式碼塊中使用 ②wait() 與  notify/notifyAll() 的執行過程 ③中斷 呼叫wait()方法進入等待佇

Java執行wait,notify,sleep,,yield,join,suspend,resume

Java中的多執行緒是一種搶佔式的機制而不是分時機制。執行緒主要有以下幾種狀態:可執行,執行,阻塞,死亡。搶佔式機制指的是有多個執行緒處於可執行狀態,但是隻有一個執行緒在執行。 當有多個執行緒訪問共享資料的時候,就需要對執行緒進行同步。執行緒中的幾個主要方法的比較:

_159_java_執行wait/notify

本文主要學習JAVA多執行緒中的 wait()方法 與 notify()/notifyAll()方法的用法。 ①wait() 與 notify/notifyAll 方法必須在同步程式碼塊中使用 ②wait() 與  notify/notifyAll() 的執行過程 ③中斷 呼叫

java執行等待喚醒機制(wait-notify

wait()、notify()、notifyAll()方法 Object類裡面提供了這幾個方法: wait():讓當前執行緒處於等待(阻塞狀態),直到其他執行緒呼叫此物件的notify()或noti

Java執行 使用waitnotify實現生產者消費者模型

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

java執行synchronized與lock、waitnotify

class Res { public String name; public String sex; public Boolean flag = false; public Lock lock = new ReentrantLock(); Condition condition = lock.new

Java執行執行間通訊--等待(wait)/通知(notify)機制,等待/通知交叉備份例項

1、什麼是等待/通知機制   等待/通知機制在生活中比比皆是,比如在就餐時就會出現,如圖所示。      廚師和服務員之間的互動要在“菜品傳遞臺”上,在這期間會有幾個問題:   1).廚師做完一道菜的時間不確定,所以廚師將菜品放到‘菜品傳遞言,上的時間也

Java執行join()方法

概要 本章,會對Thread中join()方法進行介紹。涉及到的內容包括: 1. join()介紹 2. join()原始碼分析(基於JDK1.7.0_40) 3. join()示例 來源:http://www.cnblogs.com/skywang12345/p/34792

白話理解java執行join()方法

join字面意思是加入,我理解為插隊. 舉例:媽媽在炒菜,發現沒喲醬油了,讓兒子去打醬油,兒子打完醬油,媽媽炒完菜,全家一起吃 package cn.yh.thread01; /** * * 打醬油的例子 */ public class Demo03 { public stat

細說Java 執行記憶體可見性

前言: 討論學習Java中的記憶體可見性、Java記憶體模型、指令重排序、as-if-serial語義等多執行緒中偏向底層的一些知識,以及synchronized和volatile實現記憶體可見性的原理和方法。 1、可見性介紹 可見性:一個執行緒對共用變數值的修改,能夠及時地被其他執行緒

java執行 執行協作

也是網上看的一道題目:關於假如有Thread1、Thread2、Thread3、Thread4四條執行緒分別統計C、D、E、F四個盤的大小,所有執行緒都統計完畢交給Thread5執行緒去做彙總,應當如何實現? 蒐集整理了網上朋友提供的方法,主要有: 1. 多執行緒都是Thread或

java執行鎖機制二

網上看到一個題目,題目是這樣:Java多執行緒,啟動四個執行緒,兩個執行加一,另外兩個執行減一。 針對該問題寫了一個程式,測試通過,如下: class Sync { static int count = 0; public void add() {

java執行鎖機制一

網上看了一篇關於java synchronized關鍵字使用的很好的文章,現將其簡要總結一下,加深理解。 先總結兩個規則: synchronized鎖住的是括號裡的物件,而不是程式碼。對於非static的synchronized方法,鎖的就是物件本身也就是this。 多個執行緒

java執行Phaser

java多執行緒技術提供了Phaser工具類,Phaser表示“階段器”,用來解決控制多個執行緒分階段共同完成任務的情景問題。其作用相比CountDownLatch和CyclicBarrier更加靈活,例如有這樣的一個題目:5個學生一起參加考試,一共有三道題,要求所有學生到齊才能開始考試,全部同學都

Java執行——ThreadLocal

ThreadLocal是什麼:每一個ThreadLocal能夠放一個執行緒級別的變數,也就是說,每一個執行緒有獨自的變數,互不干擾。以此達到執行緒安全的目的,並且一定會安全。 實現原理: 要了解實現原理,我們先看set方法 public void set(T value) { T

java執行Lock--顯式鎖

Lock與Synchronized簡介 Synchornized相信大家用的已經比較熟悉了,這裡我就不介紹它的用法了 Synchronized被稱為同步鎖或者是隱式鎖,隱式鎖與顯式鎖區別在於,隱式鎖的獲取和釋放都需要出現在一個塊結構中,而且是有順序的,獲取鎖的順序和釋放鎖的順序必須相反,就是說,

Java執行Executor框架

在前面的這篇文章中介紹了執行緒池的相關知識,現在我們來看一下跟執行緒池相關的框架--Executor。 一.什麼是Executor 1.Executor框架的兩級排程模型 在HotSpot VM的執行緒模型中,Java執行緒(java.lang.Thread)被一對一對映為本地作業系統執

java執行Executor

程式 程序:執行的程式 執行緒:程序中負責程式執行的執行單元,一個程序至少包括一個執行緒。 單執行緒:一個程序一個執行緒 多執行緒:一個程序多個執行緒   多執行緒是為了更好的利用CPU,提高程式執行的速度。 實現方式:繼承Thread類、實現Runnabl

java執行(二)鎖

一,鎖 在物件的建立時java會為每個object物件分配一個monitor( 監視器或者監視鎖),當某個物件的同步方法(synchronized methods )被多個執行緒呼叫時,該物件的monitor將負責處理這些訪問的併發獨佔要求。 當一個執行緒呼叫一個物件的同步方法時(sy

java執行wait sleep join yield

一,wait 是object類的方法,並不是執行緒的方法,使用此方法會使當前執行緒進入阻塞狀態,過程中執行緒會釋放cpu資源和鎖資源,獻策韓國你進入等待池,可以通過notify或者notifyall將執行緒喚醒,是執行緒重新進入就緒狀態,在wait,notify和notifyall方法使用過程中