第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時(shí)綁定郵箱和手機(jī)立即綁定
已解決430363個(gè)問題,去搜搜看,總會有你想問的

僅在添加到 HashSet 時(shí)同步是線程安全的嗎?

僅在添加到 HashSet 時(shí)同步是線程安全的嗎?

九州編程 2023-05-17 16:56:16
想象一下,有一個(gè)主線程創(chuàng)建一個(gè) HashSet 并啟動(dòng)許多工作線程將 HashSet 傳遞給它們。就像下面的代碼一樣:void main() {  final Set<String> set = new HashSet<>();  final ExecutorService threadExecutor =   Executors.newFixedThreadPool(10);  threadExecutor.submit(() -> doJob(set));} void doJob(final Set<String> pSet) {  // do some stuff  final String x = ... // doesn't matter how we received the value.  if (!pSet.contains(x)) {    synchronized (pSet) {      // double check to prevent multiple adds within different threads      if (!pSet.contains(x)) {        // do some exclusive work with x.        pSet.add(x);      }    }  }  // do some stuff}我想知道僅在 add 方法上同步是否線程安全?contains如果不同步,是否有任何可能的問題?我的直覺告訴我這很好,在離開同步塊后,對 set 所做的更改應(yīng)該對所有線程可見,但 JMM 有時(shí)可能是違反直覺的。附注:我不認(rèn)為它與How to lock multiple resources in java multithreading重復(fù) ,盡管對這兩個(gè)問題的回答可能相似,但這個(gè)問題解決了更多的特殊情況。
查看完整描述

3 回答

?
小唯快跑啊

TA貢獻(xiàn)1863條經(jīng)驗(yàn) 獲得超2個(gè)贊

我想知道僅在方法上同步是否線程安全add?contains如果不同步,是否有任何可能的問題?

簡短回答:否和是。

有兩種解釋方式:

直觀的解釋

Java 同步(以其各種形式)防止許多事情,包括:

  • 兩個(gè)線程同時(shí)更新共享狀態(tài)。

  • 一個(gè)線程試圖讀取狀態(tài),而另一個(gè)正在更新它。

  • 線程看到過時(shí)的值,因?yàn)閮?nèi)存緩存尚未寫入主內(nèi)存。

在您的示例中,同步add就足以確保兩個(gè)線程不能同時(shí)更新HashSet,并且兩個(gè)調(diào)用都將在最新HashSet狀態(tài)下運(yùn)行。

但是,如果contains也不同步,則contains調(diào)用可能會與調(diào)用同時(shí)發(fā)生add。這可能導(dǎo)致contains調(diào)用看到 的中間狀態(tài)HashSet,從而導(dǎo)致不正確的結(jié)果,或者更糟。如果調(diào)用不是同時(shí)發(fā)生的,這也會發(fā)生,因?yàn)楦臎]有立即刷新到主內(nèi)存和/或讀取線程沒有從主內(nèi)存讀取。

內(nèi)存模型解釋

JLS 指定了 Java 內(nèi)存模型,它規(guī)定了多線程應(yīng)用程序必須滿足的條件,以保證一個(gè)線程可以看到另一個(gè)線程所做的內(nèi)存更新。該模型是用數(shù)學(xué)語言表達(dá)的,并不容易理解,但要點(diǎn)是當(dāng)且僅當(dāng)從寫入到后續(xù)讀取之間存在一系列 happen before關(guān)系時(shí),才能保證可見性。如果寫入和讀取在不同的線程中,那么線程之間的同步是這些關(guān)系的主要來源。例如在

 // thread one

 synchronized (sharedLock) {

    sharedVariable = 42;

 }


 // thread two

 synchronized (sharedLock) {

     other = sharedVariable;

 }

假設(shè)線程一的代碼在線程二的代碼之前運(yùn)行,則線程一釋放鎖和線程二獲取鎖之間存在happens before關(guān)系。有了這個(gè)和“程序順序”的關(guān)系,我們就可以建立一個(gè)從寫入42到賦值到的鏈條other。這足以保證other將被分配42(或可能是變量的以后值)并且sharedVariable之前沒有任何值42被寫入它。


如果synchronized塊不在同一個(gè)鎖上同步,第二個(gè)線程可能會看到一個(gè)過時(shí)的值sharedVariable;即之前寫入的一些值42被分配給它。


查看完整回答
反對 回復(fù) 2023-05-17
?
森欄

TA貢獻(xiàn)1810條經(jīng)驗(yàn) 獲得超5個(gè)贊

該代碼對于該 synchronized (pSet) { }部分是線程安全的:


if (!pSet.contains(x)) {

  synchronized (pSet) { 

  // Here you are sure to have the updated value of pSet    

  if (!pSet.contains(x)) {

    // do some exclusive work with x.

    pSet.add(x);

  }

}

因?yàn)樵趕ynchronized對象的聲明中pSet:


一個(gè)且只有一個(gè)線程可能在這個(gè)塊中。

在其中,pSet它的更新狀態(tài)也由與 synchronized 關(guān)鍵字的 happens-before 關(guān)系保證。

因此,無論等待線程的第一個(gè)語句返回的值是什么if (!pSet.contains(x)),當(dāng)這個(gè)被等待的線程醒來并進(jìn)入語句時(shí) synchronized,它都會設(shè)置最后更新的值pSet。因此,即使前一個(gè)線程添加了相同的元素,第二個(gè)線程 if (!pSet.contains(x))也會返回false。


但是這段代碼對于if (!pSet.contains(x))在寫入Set.

根據(jù)經(jīng)驗(yàn),不應(yīng)該使用未設(shè)計(jì)為線程安全的集合來執(zhí)行并發(fā)的寫入和讀取操作,因?yàn)榧系膬?nèi)部狀態(tài)可能處于正在進(jìn)行/不一致的狀態(tài),以進(jìn)行同時(shí)發(fā)生的讀取操作一個(gè)寫操作。

雖然一些非線程安全的集合實(shí)現(xiàn)在事實(shí)中接受了這樣的用法,但這根本不能保證它總是正確的。

所以你應(yīng)該使用線程安全的Set實(shí)現(xiàn)來保證整個(gè)線程安全。

例如:


Set<String> pSet = ConcurrentHashMap.newKeySet();

這在引擎蓋下使用 a ConcurrentHashMap,因此沒有讀取鎖和最小的寫入鎖(僅在要修改的條目上而不是整個(gè)結(jié)構(gòu)上)。


查看完整回答
反對 回復(fù) 2023-05-17
?
臨摹微笑

TA貢獻(xiàn)1982條經(jīng)驗(yàn) 獲得超2個(gè)贊

,

您不知道在另一個(gè)線程添加期間哈希集可能處于什么狀態(tài)。可能正在進(jìn)行根本性的更改,例如存儲桶的拆分,因此在另一個(gè)線程添加期間包含可能會返回false ,即使該元素將存在于單線程 HashSet 中。在那種情況下,您將嘗試第二次添加元素。

更糟糕的情況:由于兩個(gè)線程同時(shí)使用的內(nèi)存中的 HashSet 處于臨時(shí)無效狀態(tài),contains可能會陷入死循環(huán)或拋出異常。


查看完整回答
反對 回復(fù) 2023-05-17
  • 3 回答
  • 0 關(guān)注
  • 205 瀏覽

添加回答

舉報(bào)

0/150
提交
取消
微信客服

購課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號