線程之間的通信

線程之間的通信

線程的通信是指線程之間以何種機制來交換信息。在命令式編程中,線程之間的通信機制有兩種:共享內(nèi)存和消息傳遞。Java線程之間的通信由Java內(nèi)存模型(簡稱為JMM)控制。

共享內(nèi)存

這種通訊模型中,不同的線程之間是沒有直接聯(lián)系的。都是通過共享變量這個“中間人”來進(jìn)行交互。而這個“中間人”必要情況下還需被保護(hù)在臨界區(qū)內(nèi)(加鎖或同步)

共享變量指的是如果一個變量在多個線程的工作內(nèi)存中都存在副本,那么這個變量就是這幾個線程的共享變量。

  • 每個線程都有自己獨立的工作內(nèi)存,里面保存該線程使用到的變量的副本(主內(nèi)存中該變量的一份拷貝)。
  • 線程對共享變量的所有操作都必須在自己的工作內(nèi)存中進(jìn)行,不能直接從主內(nèi)存中讀取。
  • 不同線程之間無法直接訪問其他線程工作內(nèi)存中的變量,線程間變量值的傳遞需要通過主內(nèi)存來完成。

從上圖看,如果線程A對線程變量的修改想要被線程B及時看到(實現(xiàn)通信),必須要經(jīng)過如下兩個步驟:

①將本地內(nèi)存A中更新過的共享變量刷新到主內(nèi)存中
②將主內(nèi)存中最新的共享變量的值更新到本地內(nèi)存B中

從整體來看,這兩個步驟實質(zhì)上是線程A在向線程B發(fā)送消息,而且這個通信過程必須要經(jīng)過主內(nèi)存。JMM通過控制主內(nèi)存與每個線程的本地內(nèi)存之間的交互,來保證可見性。

消息傳遞
??在消息傳遞的并發(fā)模型里,線程之間沒有公共狀態(tài),采取的是線程之間的直接通信,不同的線程之間通過顯式的發(fā)送消息來達(dá)到交互目的。
??消息傳遞最有名的方式應(yīng)該是actor模型了。在這種模型下,一切都是actor,所有的actor之間的通信都必須通過傳遞消息才能達(dá)到。每個actor都有一個收件箱(消息隊列)用來保存收到其他actor傳遞來的消息。actor自己也可以給自己發(fā)送消息。

兩種模型的同步機制

并發(fā)模型 同步機制
共享內(nèi)存 同步是顯式進(jìn)行的。程序員必須顯式指定某個方法或某段代碼需要在線程之間互斥執(zhí)行。
消息傳遞(actor) 由于消息的發(fā)送必須在消息的接收之前,因此同步是隱式進(jìn)行的。

競爭現(xiàn)象
??如果多個線程共享一個對象,如果它們同時修改這個共享對象,這就產(chǎn)生了競爭現(xiàn)象。如下圖所示,線程A和線程B共享一個對象obj。假設(shè)線程A從主存讀取Obj.count變量到自己的CPU緩存,同時,線程B也讀取了Obj.count變量到它的CPU緩存,并且這兩個線程都對Obj.count做了加1操作。此時,Obj.count加1操作被執(zhí)行了兩次,不過都在不同的CPU緩存中。??
??如果這兩個加1操作是串行執(zhí)行的,那么Obj.count變量便會在原始值上加2,最終主存中的Obj.count的值會是3。然而下圖中兩個加1操作是并行的,不管是線程A還是線程B先flush計算結(jié)果到主存,最終主存中的Obj.count只會增加1次變成2,盡管一共有兩次加1操作。


??要解決上面的問題就要實現(xiàn)核心代碼的原子性,保證同一個時刻只能有一個線程進(jìn)入代碼競爭區(qū)。

同步與互斥

相交進(jìn)程之間的關(guān)系主要有兩種:同步與互斥(一定要記?。翰皇峭胶彤惒剑?/p>

同步、互斥
??有些線程之間存在協(xié)作關(guān)系,需要按照一定的協(xié)議來協(xié)同完成某項任務(wù),比如典型的生產(chǎn)者-消費者模式。這種情況下就需要用到Java提供的線程之間的等待-通知機制。
??同步是線程之間的一種通信機制,一個線程做一件事情,它會以某種方式通知其他線程我做完了,也就是在發(fā)出一個功能調(diào)用時,在沒有得到結(jié)果之前,該調(diào)用就不返回,必須一件一件事做,等前一件做完了才能做下一件事。
??同步有幾個方面的作用。最廣為人知的就是互斥。互斥就是同一時間,只能有一個線程對公共的資源進(jìn)行操作,具有唯一性和排它性。好比對數(shù)據(jù)庫的操作,允許同時讀操作但不允許同時寫操作。就像女神在同一時間只能和一個男主角約會?;コ庠L問是用來實現(xiàn)原子操作的一個前提基礎(chǔ)。
??但是同步的含義比互斥更廣。同步保證了一個線程在同步塊之前或者在同步塊中的一個內(nèi)存寫入操作以可預(yù)知的方式對其他相同監(jiān)視器的線程可見。所以,同步是一種更為復(fù)雜的互斥,而互斥是一種特殊的同步。
??也就是說互斥是兩個線程之間不可以同時運行,他們會相互排斥,必須等待一個線程運行完畢,另一個才能運行。而同步也是不能同時運行,但他是必須要按照某種次序來運行相應(yīng)的線程(也是一種互斥)!
??所以同步是在互斥的基礎(chǔ)上(大多數(shù)情況),通過其它機制實現(xiàn)訪問者對資源的有序訪問。在大多數(shù)情況下,同步已經(jīng)實現(xiàn)了互斥,特別是所有寫入資源的情況必定是互斥的。少數(shù)情況是指可以允許多個訪問者同時訪問資源。

  • 當(dāng)我們退出了同步塊,我們就釋放了這個監(jiān)視器,這個監(jiān)視器有刷新緩沖區(qū)到主內(nèi)存的效果,因此該線程的寫入操作能夠為其他線程所見。
  • 在我們進(jìn)入一個同步塊之前,我們需要獲取監(jiān)視器,監(jiān)視器有使本地處理器緩存失效的功能,因此變量會從主存重新加載,于是其他線程對共享變量的修改對當(dāng)前線程來說就變得可見了。

異步
??異步和同步是相對的。異步就是在發(fā)出一個功能調(diào)用的時候,不需要等待響應(yīng),繼續(xù)進(jìn)行它該做的事情,一旦得到響應(yīng)了過后給予一定的處理,但是不影響正常的處理過程的一種方式。比如有一個線程A,在A執(zhí)行的過程中,同樣需要B提供一些相關(guān)數(shù)據(jù)或者操作,當(dāng)A向B發(fā)送一個請求或者對B進(jìn)行調(diào)用操作過后,A不需要繼續(xù)等待,而是執(zhí)行A自己應(yīng)該做的事情,一旦B有了響應(yīng)過后會通知A,A接受到該異步請求的響應(yīng)的時候會進(jìn)行相關(guān)的處理,這種情況下A的操作就是一個簡單的異步操作。
??異步和多線程并不是一個同等關(guān)系,異步是最終目的,多線程只是我們實現(xiàn)異步的一種手段。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,789評論 11 349
  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,895評論 0 11
  • 你的發(fā)長成一棵綠色的藤 綠色的藤又開出粉艷的花 粉艷的花結(jié)出殷紅的果 迸落在我的心上,扎了根 沒有痛楚,只有酥癢的...
    墨鏡123456閱讀 391評論 7 7

友情鏈接更多精彩內(nèi)容