首頁  >  文章  >  Java  >  Java如何使用wait()與notify()方法操作共享資源的實例

Java如何使用wait()與notify()方法操作共享資源的實例

黄舟
黄舟原創
2017-10-11 09:52:411358瀏覽

這篇文章主要為大家詳細介紹了Java使用wait() notify()方法操作共享資源,具有一定的參考價值,有興趣的小伙伴們可以參考一下

Java多個線程共享資源;

  1)wait()、notify()和notifyAll()方法是本地方法,並且為final方法,無法被重寫。

  2)呼叫某個物件的wait()方法能讓目前執行緒阻塞,且目前執行緒必須擁有此物件的monitor(即鎖,或稱為管程)

  3)調用某個物件的notify()方法能夠喚醒一個正在等待這個物件的monitor的線程,如果有多個執行緒都在等待這個物件的monitor,則只能喚醒其中一個執行緒;

  4)調用notifyAll()方法能夠喚醒所有正在等待這個物件的monitor的執行緒; 

        在Java中,並以沒有類似PV操作、進程互斥等相關的方法的方法。 JAVA的進程同步是透過synchronized()來實現的,需要說明的是,Java的synchronized()方法類似於作業系統概念中的互斥記憶體區塊,在Java中的Object類別物件中,都是帶有一個內存鎖的,在有線程獲取該內存鎖後,其它線程無法訪問該內存,從而實現Java中簡單的同步、互斥操作。明白這個原理,就能理解為什麼synchronized(this)與synchronized(static XXX)的區別了,synchronized就是針對內存區塊申請內存鎖,this關鍵字代表類的一個對象,所以其內存鎖是針對相同對象的互斥操作,而static成員屬於類別專有,其記憶體空間為該類別所有成員共有,這就導致synchronized()對static成員加鎖,相當於對類別加鎖,也就是在該類別的所有成員間實作互斥,在同一時間只有一個執行緒可存取該類別的實例。如果需要在執行緒間相互喚醒就需要藉助Object類別的wait()方法及nofity()方法。

說了這麼一堆,可能似懂非懂,那麼接下來用一個例子來說明問題,用多線程實現連續的1,2,1,2,1,2,1,2, 1,2輸出。


package com.study.thread;
/**
 * 多线程
 * @ClassName: PrintFile 
 * @date 2017年10月10日 下午4:05:04
 */
public class PrintFile implements Runnable{
  //当前线程id
  private int id ;
  //共享资源
  public byte[] res ;
  
  //如果类里写了有参构造器,而任然想保留无参数构造方法,则必须显式的写出该方法。
  public PrintFile() {
    super();
//    System.out.println("我是构造器"); 
  }

  public PrintFile(int id, byte[] res) {
    //构造器中使用super()/this(),必须放在第一行。
    this(); 
    this.id = id;
    this.res = res;
  }

  //静态计数器
  public static int count = 5;
  
  @Override
  public void run() {
    synchronized (res) {
      while(count-->=0){
        try {
          res.notify();//唤醒其他线程中的某一个(唤醒等待res的其他线程,当前线程执行完后要释放锁)
          System.out.println("当前线程id值:"+id);
          
          res.wait();//当前线程阻塞,等待被唤醒
          System.out.println("现在执行的线程是"+Thread.currentThread().getName()+",--wait()后的代码继续执行:"+id);
          
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      return; 
    }
  }
}

測試:


#
package com.study.thread;

public class PrintFileTest {
 public static void main(String[] args) {
  byte[] res = new byte[]{0,1,2};//共享资源
  PrintFile p1 = new PrintFile(1, res);
  PrintFile p2 = new PrintFile(2, res);
  
  Thread t1 = new Thread(p1, "a");
  Thread t2 = new Thread(p2, "b");
    
  t1.start();
  t2.start();
 }
}

結果:

目前執行緒id值:1
目前執行緒id值:2
現在執行的執行緒是a,--wait()後的程式碼繼續執行:1
目前執行緒id值:1
現在執行的執行緒是b,-- wait()後的程式碼繼續執行:2
目前執行緒id值:2
現在執行的執行緒是a,--wait()後的程式碼繼續執行:1
目前執行緒id值:1
現在執行的執行緒是b,--wait()後的程式碼繼續執行:2
目前執行緒id值:2
現在執行的執行緒是a,--wait()後的程式碼繼續執行:1

下面解釋為什麼會出現這樣的結果:

首先1、2號執行緒啟動,這裡假設1號執行緒先執行run方法取得資源(其實是不確定的),取得物件a的鎖,進入while迴圈(用於控制輸出幾輪):

1、此時物件呼叫它的喚醒方法notify(),意思是這個同步區塊執行完後它要釋放鎖,把鎖交給等待a資源的執行緒;

2、輸出1;

3、該物件執行等待方法,意思是此時此刻起擁有這個物件鎖的執行緒(也就是這裡的1號執行緒)釋放CPU控制權,釋放鎖,且執行緒進入阻塞狀態,後面的程式碼暫時不執行,因未執行完同步區塊,所以1也沒起作用;

# 4.在這之前的某時刻線程2運行run方法,但苦於沒有獲得a對象的鎖,所以無法繼續運行,但3步驟之後,它獲得了a的鎖,此時執行a的喚醒方法notify() ,同理,意思是這個同步區塊執行完後它要釋放鎖,把鎖交給等待a資源的執行緒;

#5、輸出2;

6、執行a的等待方法,意思是此時此刻起擁有這個物件鎖的執行緒(也就是這裡的2號執行緒)釋放CPU控制權,釋放鎖,並且執行緒進入阻塞狀態,後面的程式碼暫時不執行,因未執行完同步區塊,所以2號執行緒的4步驟的喚醒方法也沒起作用;

7、此時1號執行緒執行到3步驟,發現物件鎖定沒有被使用,所以繼續執行3步驟中wait方法後面的程式碼,於是輸出:------執行緒1取得鎖,wait()後的程式碼繼續運作:1;

8、此時while迴圈滿足條件,繼續執行,所以,再執行1號執行緒的喚醒方法,意思是這個同步區塊執行完後它要釋放鎖定;

9、輸出1;

10、執行等待方法,執行緒1阻塞,釋放資源鎖定;

11、此時執行緒2又獲得了鎖,執行到步驟6,繼續執行wait方法後面的程式碼,所以輸出:------執行緒2取得鎖,wait()後的程式碼繼續運作:2;

12、繼續執行while循環,輸出2;

··· ···

透過上述步驟,相信大家已經明白這兩個方法的使用了,但該程式還存在一個問題,當while循環不滿足條件時,肯定會有執行緒還在等待資源,所以主執行緒一直不會終止。當然這個程式的目的只是為了給大家示範這兩個方法怎麼用。

總結:

#

 wait()方法與notify()必須與synchronized(resource)一起使用。也就是wait與notify針對已經取得了resource鎖的執行緒進行操作,從語法角度來說就是Obj.wait(),Obj.notify必須在synchronized(Obj){...}語句區塊內。從功能上來說wait()執行緒在取得物件鎖後,主動釋放CPU控制權,主動釋放物件鎖,同時本執行緒休眠。直到有其它線程呼叫物件的notify()喚醒該線程,才能繼續取得物件鎖定,並繼續執行。對應的notify()就是對物件鎖的釋放操作。 【因此,我們可以發現,wait和notify方法都可以釋放物件的鎖,但wait同時釋放CPU控制權,即它後面的程式碼停止執行,執行緒進入阻塞狀態,而notify方法不立刻釋放CPU控制權,而是在對應的synchronized(){}語句塊執行結束,再自動釋放鎖定。 】釋放鎖定後,JVM會在等待resoure的線程中選取一線程,賦予其物件鎖定,喚醒線程,繼續執行。這樣就提供了在執行緒間同步、喚醒的操作。 Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權,主要的區別在於Object.wait()在釋放CPU同時,釋放了物件鎖的控制,而在同步區塊中的Thread.sleep()方法並不會釋放鎖,僅釋放CPU控制權。

以上是Java如何使用wait()與notify()方法操作共享資源的實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn