国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java notify和notifyAll的區(qū)別和相同

2019-11-26 13:50:27
字體:
供稿:網(wǎng)友

經(jīng)常在往上逛,關(guān)于在java中notify和notifyAll,經(jīng)常有人有以下的說法:

notify只會通知一個在等待的對象,而notifyAll會通知所有在等待的對象,并且所有對象都會繼續(xù)運行

并且,好像都有例子可以證明。上面的說法,可以說對,也可以說不對。究其原因,在于其中有一點很關(guān)鍵,官方的說法如下所示:

wait,notify,notifyAll:

此方法只應(yīng)由作為此對象監(jiān)視器的所有者的線程來調(diào)用。通過以下三種方法之一,線程可以成為此對象監(jiān)視器的所有者

通過執(zhí)行此對象的同步實例方法。
通過執(zhí)行在此對象上進(jìn)行同步的 synchronized 語句的正文。
對于 Class 類型的對象,可以通過執(zhí)行該類的同步靜態(tài)方法。 
 一次只能有一個線程擁有對象的監(jiān)視器。

以上說法,摘自javadoc。意思即,在調(diào)用中,必須持有對象監(jiān)視器(即鎖),我們可以理解為需要在synchronized方法內(nèi)運行。那么由此話的隱含意思,即如果要繼續(xù)由同步塊包含的代碼塊,需要重新獲取鎖才可以。這句話,在javadoc中這樣描述:

wait

此方法導(dǎo)致當(dāng)前線程(稱之為 T)將其自身放置在對象的等待集中,然后放棄此對象上的所有同步要求。出于線程調(diào)度
目的,在發(fā)生以下四種情況之一前,線程 T 被禁用,且處于休眠狀態(tài):
其他某個線程調(diào)用此對象的 notify 方法,并且線程 T 碰巧被任選為被喚醒的線程。
其他某個線程調(diào)用此對象的 notifyAll 方法。
其他某個線程中斷線程 T。
大約已經(jīng)到達(dá)指定的實際時間。但是,如果 timeout 為零,則不考慮實際時間,在獲得通知前該線程將一直等待。
 然后,從對象的等待集中刪除線程 T,并重新進(jìn)行線程調(diào)度。然后,該線程以常規(guī)方式與其他線程競爭,以獲得在該對
象上同步的權(quán)利;一旦獲得對該對象的控制權(quán),該對象上的所有其同步聲明都將被恢復(fù)到以前的狀態(tài),這就是調(diào)用 wait
 方法時的情況。然后,線程 T 從 wait 方法的調(diào)用中返回。所以,從 wait 方法返回時,該對象和線程 T 的同步狀態(tài)與調(diào)
用 wait 方法時的情況完全相同。

即必須重新進(jìn)行獲取鎖,這樣對于notifyAll來說,雖然所有的線程都被通知了。但是這些線程都會進(jìn)行競爭,且只會有一個線程成功獲取到鎖,在這個線程沒有執(zhí)行完畢之前,其他的線程就必須等待了(只是這里不需要再notifyAll通知了,因為已經(jīng)notifyAll了,只差獲取鎖了)有如下一個代碼,可以重現(xiàn)這個現(xiàn)象。

首先,定義一個可以運行的線程類,如下所示:

private static final Object obj = new Object();  static class R implements Runnable {    int i;     R(int i) {      this.i = i;    }     public void run() {      try {        synchronized(obj) {          System.out.println("線程-> " + i + " 等待中");          obj.wait();          System.out.println("線程-> " + i + " 在運行了");          Thread.sleep(30000);        }      } catch(Exception e) {        e.printStackTrace();      }    }  }

注意上面的run方法內(nèi)部,我們在wait()之后,打印一句話,然后將當(dāng)前代碼,暫停30秒。關(guān)于sleep方法,是這樣描述的:
該線程不丟失任何監(jiān)視器的所屬權(quán)。
即仍然持有鎖。

然后,定義一個main方法來運行這些線程,如下所示:

Thread[] rs = new Thread[10];    for(int i = 0;i < 10;i++) {      rs[i] = new Thread(new R(i));    }    for(Thread r : rs) {      r.start();    }     Thread.sleep(5000);    synchronized(obj) {      obj.notifyAll();    }

我們定義了10個線程,然后全部運行之。因為有wait,10個線程都會在打印出 “開始運行”之后等待。然后main方法調(diào)用notifyAll。這里的輸出就會出現(xiàn)如下的輸出:

線程-> 0 等待中
線程->  4 等待中
線程->  5 等待中
線程->  3 等待中
線程->  2 等待中
線程->  1 等待中
線程->  6 等待中
線程->  7 等待中
線程->  8 等待中
線程->  9 等待中
線程->  9 在運行了
...30秒之內(nèi),不會有其他輸出

在上面的輸出中,在wait之后,只有一個線程輸出了”在運行了”語句,并且在一段時間內(nèi)(這里為30秒),不會有其他輸出。即表示,在當(dāng)前代碼持有鎖之間,其他線程是不會輸出的。

最后結(jié)論就是:被wait的線程,想要繼續(xù)運行的話,它必須滿足2個條件:

由其他線程notify或notifyAll了,并且當(dāng)前線程被通知到了

經(jīng)過和其他線程進(jìn)行鎖競爭,成功獲取到鎖了2個條件,缺一不可。其實在實現(xiàn)層面,notify和notifyAll都達(dá)到相同的效果,會有一個線程繼續(xù)運行。但notifyAll免去了,線程運行完了通知其他線程的必要,因為已經(jīng)通知過了。什么時候用notify,什么時候使用notifyAll,這就得看實際的情況了。

以上就是對Java notify和NotifyAll的資料整理,后續(xù)繼續(xù)補(bǔ)充相關(guān)資料謝謝大家對本站的支持!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 昭苏县| 长武县| 大庆市| 新宁县| 德化县| 平顺县| 宁德市| 游戏| 新源县| 额尔古纳市| 台南市| 开平市| 乃东县| 合山市| 德阳市| 莒南县| 喀喇沁旗| 洛阳市| 新昌县| 临高县| 青神县| 论坛| 固镇县| 盐津县| 鄂温| 昌图县| 江西省| 浠水县| 从江县| 康乐县| 淮南市| 高州市| 双鸭山市| 谷城县| 盐津县| 岑溪市| 大名县| 璧山县| 建昌县| 富阳市| 上犹县|