国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java并發編程筆記(二)

NickZhou / 2646人閱讀

摘要:本文探討并發中的其它問題線程安全可見性活躍性等等。當閉鎖到達結束狀態時,門打開并允許所有線程通過。在從返回時被叫醒時,線程被放入鎖池,與其他線程競爭重新獲得鎖。

本文探討Java并發中的其它問題:線程安全、可見性、活躍性等等。

在行文之前,我想先推薦以下兩份資料,質量很高:
極客學院-Java并發編程
讀書筆記-《Java并發編程實戰》

線程安全
《Java并發編程實戰》中提到了太多的術語,比如各種XX性。而安全性我覺得這個概念并不妥。計算機術語中的線程安全大家一說就懂,但老是生造概念就不好了。又例如,活躍性,就是避免饑餓和死鎖唄!

線程安全問題就是多線程時結果受執行順序影響,要解決就要讓相關操作具有原子性。這個上過操作系統原理的肯定都知道。至于原子性,不再解釋。

那么,Java給出了哪些工具來保證原子性和線程安全?

內置鎖

synchronized關鍵字。內置鎖可以作用在方法、代碼塊中,作用在方法時表示用該類的當前實例(this)作為鎖給方法體加鎖。內置鎖的實現是通過編譯器加入monitor_enter和montior_exit指令,在虛擬機遇到前者時嘗試獲取鎖,把鎖的計數器加1;遇到后者時,將鎖計數器減1,鎖計數器為0時,鎖被釋放。

內置鎖一度是java中進行同步的唯一方法,很多遺留方法還是使用了內置鎖進行同步,比如著名的VectorCollections里面的同步包裝器(如Collections.synchronizedMap(hashmap))等。

關于它和Lock的比較,詳見此文。結論是,建議優先使用synchronized來進行同步。

顯式鎖

顯式鎖的頂層接口為Lock,提供了ReenterantLock, ReadWriteLock等實現。常見用法如下:

Lock lock = new ReentrantLock();
...
lock.lock();
try {
// 方法體
} 
...
finally {
    lock.unlock();
}

所謂可重入就是鎖的獲得是以線程為單位的,同一線程獲得鎖后可以重復進入鎖。鎖會保存被持有的計數。

信號量、柵欄、閉鎖

信號量Semaphore,相當于允許進入數量大于1的鎖。
閉鎖Latch,實現類CountDownLatch。相當于一個門,閉鎖到達結束狀態前,門一直關著,所有線程都不能通過。當閉鎖到達結束狀態時,門打開并允許所有線程通過。
柵欄Barrier,所有線程都等待時才打開放行。

CAS 與樂觀鎖

現代CPU支持一種CAS(Compare And Swap)指令,可以在一個指令內完成設置和沖突檢測,從而實現了高效的原子性。CAS指令接受三個參數(v, expectedValue, newValue)。如果變量v的值和expectedValue相等,那么就將v賦值為newValue;如果和expectedValue不相等,就返回失敗。

為何CAS的效率更高?采用互斥同步策略的最主要問題就是進行線程阻塞和喚醒所帶來的性能問題,因而這種同步又稱為阻塞同步,它屬于一種悲觀的并發策略(悲觀鎖),即線程獲得的是獨占鎖。獨占鎖意味著其他線程只能依靠阻塞來等待線程釋放鎖。而在 CPU 轉換線程阻塞時會引起線程上下文切換,當有很多線程競爭鎖的時候,會引起 CPU 頻繁的上下文切換(由此導致內核態和用戶態切換)導致效率很低。

而基于沖突檢測(CAS)的樂觀并發策略,通俗地講就是先進性操作,如果沒有其他線程爭用共享數據,那操作就成功了,如果共享數據被爭用,產生了沖突,那就再進行其他的補償措施(最常見的補償措施就是不斷地重試,直到試成功為止),這種樂觀的并發策略不需要把線程掛起,因此這種同步被稱為非阻塞同步。

Java 5.0之后才支持CAS,并用它實現了一些原子變量類,如AtomicInteger//AtomicReference等等。更重要的是,前面提到的所有鎖機制幾乎都使用了CAS來做性能優化。

線程間協作

這里的線程間協作是指通過一些機制使得線程可以彼此等待、喚醒,從而能夠合作。例如,在生產者-消費者模型中,如果生產者向隊列中放入一個新任務,可以立刻喚醒一個等待在此的消費者,這便是協作。

首先是基于內置鎖和Object類的wait()notify()notifyAll()方法。
在java中,每個對象都有兩個池,鎖池和等待池。

鎖池:要進入synchronized同步塊的線程,如果此同步塊的鎖(是一個對象)被其他線程持有,則顯然線程不能執行下去。線程將被放入該鎖對象的鎖池中,在鎖池中的線程都在競爭這個鎖。

等待池:調用了鎖對象的wait()方法后,就進入了等待池。在等待池中的線程不去競爭鎖,而是等待被鎖對象的notify()notifyAll()喚醒,之后再進入鎖池,開始競爭鎖。

所以,鎖池中的線程相當于睡著了,而等待池中的線程則進入了第二層睡眠!(如果你看過《盜夢空間》的話~)

再來講這三個方法就好理解了:

Object.wait()
將當前線程放到鎖的等待池,直到接到通知(其他線程調用 notify()方法或 notifyAll()方法)或被中斷。在調用 wait()之前,線程必須要獲得該對象鎖,即只能在同步塊中調用 wait()方法。進入 wait()方法后,當前線程釋放鎖。在從 wait()返回時(被叫醒時),線程被放入鎖池,與其他線程競爭重新獲得鎖。

Object.notify()
也必須在同步方法或同步塊中調用,用來“叫醒”鎖的等待池中的其他線程。如果有多個線程等待,則任意挑選出其中一個,扔到鎖池中,但不驚動其他同樣在等待被該對象notify的線程們。這里的“叫醒”只是叫醒第二層睡眠,還沒完全醒。

Object.notifyAll()
把所有的鎖等待池中的線程扔到鎖池中。

說了這么多,這幾個方法有什么用?還是生產者-消費者問題,隊列數據的正確性需要同步機制來確保,而兩個線程何時生產,何時取走就需要線程間協作了。詳見此文。

最后,實際上這幾個方法已經過時了。如果想實現等待阻塞的功能,應該使用更好用的LockCondition,與前面的組合如出一轍。

關于LockCondition的例子,見此回答。

可見性與同步、volatile

可見性指的是,一個變量被一個線程更改后,另一個線程在讀取時由于時間順序,可能得到的最新的有效值,也可能得到的是舊的無效值。

因此,同步的意義不僅僅在于寫,還在于讀。只要在讀的時候也進行同步操作(加鎖),就肯定能保證可見性。

另一方面,使用volatile關鍵字可以實現輕量級的可見性。volatile關鍵字會禁止所修飾的變量被指令重排序和優化成寄存器值從而不對所有線程可見。由于Java保證最低可見性(CPU設置一個變量會是個原子操作,不會出現設置到一半就被讀取,從而得到一個隨機值的情況),因而volatile可以實現非常高效的可見性。

但是volatile的局限也是有的:它只能用于賦值操作,如果是i++這種組合操作,結果依賴于之前的值,就不再能保證原子性了,因而無法保證準確。這時,只能采用加鎖操作(或者CAS的沖突重試,總之要保證原子性)。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68534.html

相關文章

  • 【J2SE】java并發編程實戰 讀書筆記( 一、、三章)

    摘要:發布的對象內部狀態可能會破壞封裝性,使程序難以維持不變性條件。不變性線程安全性是不可變對象的固有屬性之一。可變對象必須通過安全方式來發布,并且必須是線程安全的或者有某個鎖保護起來。 線程的優缺點 線程是系統調度的基本單位。線程如果使用得當,可以有效地降低程序的開發和維護等成本,同時提升復雜應用程序的性能。多線程程序可以通過提高處理器資源的利用率來提升系統的吞吐率。與此同時,在線程的使用...

    QLQ 評論0 收藏0
  • 阿里 2021 版最全 Java 并發編程筆記,看完我才懂了“內卷”的真正意義

    摘要:純分享直接上干貨操作系統并發支持進程管理內存管理文件系統系統進程間通信網絡通信阻塞隊列數組有界隊列鏈表無界隊列優先級有限無界隊列延時無界隊列同步隊列隊列內存模型線程通信機制內存共享消息傳遞內存模型順序一致性指令重排序原則內存語義線程 純分享 , 直接上干貨! 操作系統并發支持 進程管理內存管...

    不知名網友 評論0 收藏0
  • Java并發編程的藝術】第一章讀書筆記

    摘要:前言并發編程的目的是讓程序跑的更快,但并不是啟動更多的線程,這個程序就跑的更快。盡可能降低上下文切換的次數,有助于提高并發效率。死鎖并發編程中的另一挑戰是死鎖,會造成系統功能不可用。 前言 并發編程的目的是讓程序跑的更快,但并不是啟動更多的線程,這個程序就跑的更快。有以下幾種挑戰。 挑戰及方案 上下文切換 單核CPU上執行多線程任務,通過給每個線程分配CPU時間片的方式來實現這個機制。...

    馬忠志 評論0 收藏0
  • 后臺開發常問面試題集錦(問題搬運工,附鏈接)

    摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...

    spacewander 評論0 收藏0
  • 后臺開發常問面試題集錦(問題搬運工,附鏈接)

    摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...

    xfee 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<