我正在實現一個程序,其中主線程將各種消息推送到工作線程,並且工作線程將工作結果推回到主線程。BlockingQueue在任何時候都會與所有線程相同嗎?
爲此,我計劃使用兩個隊列,一個推到工作者線程,另一個從隊列中拉出。
據我所知,線程緩存對象,因此如果它們將一個對象指向另一個線程可能的隊列poll()
,則此更改可能不會立即可見,即可能不會同步。它是否正確?
但是,如果我要使用BlockingQueue
,則此隊列上的所有操作都應該立即對所有線程可見?
我正在實現一個程序,其中主線程將各種消息推送到工作線程,並且工作線程將工作結果推回到主線程。BlockingQueue在任何時候都會與所有線程相同嗎?
爲此,我計劃使用兩個隊列,一個推到工作者線程,另一個從隊列中拉出。
據我所知,線程緩存對象,因此如果它們將一個對象指向另一個線程可能的隊列poll()
,則此更改可能不會立即可見,即可能不會同步。它是否正確?
但是,如果我要使用BlockingQueue
,則此隊列上的所有操作都應該立即對所有線程可見?
這個答案假定您正在討論線程安全的數據結構或構造。如果您使用常規的非線程安全字段,它不能保證它可以正常工作,但也不能保證它不起作用。注意:這是針對線程安全的單元測試有用的,但不是簡單的證明。
此更改可能不會立即顯示,即它可能不會同步。它是否正確?
是。延遲時間通常在50 - 100納秒左右,因此可能幾乎是立即的。
這個隊列上的所有操作都應該立即對所有線程可見?
這在真機中是不可能的。你可以說的是,你應該永遠不會因爲可見性問題而得到一個錯誤,其中大部分是非常輕微的延遲。 (但是因爲它產生的垃圾,你可以得到在最壞的情況下,全GC的延遲,如果機器沒有得到在這種情況下,延遲可能是幾小時或幾天休眠)
如果線程A和B具有對同一對象的引用,並且A更改了該對象,那麼此更改將保證最終在線程B中可見?我認爲線程可以更長時間地保存對象的緩存,例如,如果你想在它們之間共享一個對象的變量,但仍然保證這兩個線程都可以看到變化,你必須使用'volatile'關鍵詞。 – corazza
正確,並且保證您不會看到不一致的更新。這是更重要的功能。這個改變對於兩個線程都是可見的,但是在CPU中不會立即發生。最小時鐘週期爲〜0.3 ns,如果設置了一個易失性變量並在另一個線程中讀取它,則至少需要70個時鐘週期。如果你寫入一個volatile並在* same *線程中讀取,它將需要約5 ns。對於像分配對象的隊列這樣的複雜數據結構,這需要50 - 100 ns的量級。 –
所有其他答案/評論都會顯示*「如果線程A和B對同一個對象有引用,並且A更改了該對象,那麼這個更改將保證最終在線程B中可見?」* - is ** false **, 不正確!例如。它與你提到的延遲無關,而是與* Java *中的線程一起實現。我認爲你回答了有關線程的問題*一般* ... – corazza
的javadoc爲BlockingQueue
接口這樣說:
「
BlockingQueue
」實現是線程安全的,所有排隊方法都是通過內部鎖或其他形式的併發控制自動實現其效果。
你因此保證改變一個線程隊列做出「立即」 ......對其他線程可見的內存模型的意義。照顧到put
和poll
呼叫的同步。 (任何緩存效果都由javadoc中提到的機制來處理。)
當然,這並不意味着它們都會在語義級別看到更改。這取決於諸如線程調度之類的事情。 (如果一個對象被添加到一個隊列中,只有一個線程可以刪除它,如果它在第二個線程看到隊列狀態之前這麼做了......無論出於何種原因......第二個線程不會注意到該條目已被添加,然後被刪除。)
「立即」的粒度也未指定。例如,如果JVM只使用一個CPU /內核,則線程只能在調用put
調用的線程被解除調度之後從poll
返回。另外,緩存內存緩存中的變化需要花費大量的時間。
1 - 假定實現類滿足「契約」 ......但如果沒有它是越野車。
根據你原來的問題和評論,你混淆了兩個不同的東西。
任何實現BlockingQueue
的線程安全並正確發佈。實現該接口的類的內部各不相同,因此聲稱「此隊列上的所有操作都應該立即對所有線程可見」可能不適用於直接的某些定義。 「非常快」將是一個更加貼切的描述;它可能沒有使用阻塞/鎖定機制,並且總是涉及線程調度。
安全發佈(線程緩存)與線程在Java中的工作方式有關,是一個更大的話題。必須安全地發佈對該BlockingQueue
的引用,以供所有線程查看。在大多數情況下,當使用這樣的結構時,你可能做的是正確的事情。也就是說,要麼將參考文獻傳遞給Runnable
構造函數,要麼將其創建爲final
變量。但是,真的,只要你沒有得到一個NullPointerException
...線程可以看到它。
當您將對象傳遞給多個線程之後,在對象內部實例化某個對象後出現安全發佈問題。
該主題事實上的參考文獻是Java Concurrency in Practice,非常值得擁有。
好的,這聽起來不錯...但是我我擔心我在隊列中推送的對象不會被同步......我可能是錯的,但是如果線程B調用的run方法創建一個對象並將其放入隊列中,那麼當前正在運行的對象線程A有一個引用,這個對象是否會在線程之間同步?就像它的引用一樣?例如,'run'創建一個名爲'text'的單個公共字段的'Message',並將該字段設置爲一個消息「',並將其推送到隊列中(所有這些都在同步塊中),那個對象是否與其他線程相同? – corazza
好吧,這是在討論安全發佈。你保證A會看到所有東西這是在它從隊列中獲得引用之前發生的。可見性進入onc e這兩個線程都有對該對象的引用,然後你修改該對象。不能保證在A中看到通過B中某種同步機制完成的對象任何更改(反之亦然)。例如,如果在A具有對象的引用之後,您要在B中爲「text」分配不同的'String'。例如,將該字段設置爲「揮發性」,可以保證A看到了變化。 –
'BLockngQueue'位於'Java.util.concurrent'包中。整個程序包用於併發(線程安全)編程。任何實現該接口的東西都是爲了你想要的。 –
@BrianRoach可能會也可能不會回答我的問題......我讀了類的文檔,但它提到了它只是阻塞,直到另一個線程準備從它讀取,而不是它會導致線程刷新其緩存。 – corazza
@yannbane再次忽略,整個包是用於併發編程的,安全發佈(線程緩存)涉及給定類中的對象的引用*,而不一定是它的內容。一個'BlockingQueue'完全是線程安全的,但它在你所說的內容上的可見性取決於包含的類是正確發佈它(通過'final'變量或'volatile'變量)還是僅僅依靠發生 - 行爲之前。 –