摘要:不可變如絕對線程安全就是滿足并發編程實戰中對線程安全的定義在中標注自己是線程安全的類,大都不是絕對的線程安全。如的和二實現線程安全的方法互斥同步互斥是因同步是果互斥是方法同步是目的。
一.到底什么叫線程安全:
java并發編程實戰中對線程安全的定義是:當多個線程訪問一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他的協調操作,調用這個對象的行為都可以獲得正確的結果,那這個對象是線程安全的”。這個定義比較嚴格,一般我們都會將其弱化。
按照線程安全的“安全程度”由強至弱來排序,我們可以將Java語言中各種操作共享的數據分為以下5類:不可變、絕對線程安全、相對線程安全、線程兼容和線程對立。
1.不可變:如String
2.絕對線程安全就是滿足java并發編程實戰中對線程安全的定義
在javaAPI中標注自己是線程安全的類,大都不是絕對的線程安全。如Vector,它的add,get,size方法都加了synchronized修飾的,但請看下面一段代碼:
private static Vectorvector = new Vector (); public static void main(String[] args) { while (true) { for (int i = 0; i < 10; i++) { vector.add(i); } Thread removeThread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < vector.size(); i++) { vector.remove(i); } } }); Thread printThread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < vector.size(); i++) { System.out.println((vector.get(i))); } } }); removeThread.start(); printThread.start(); //不要同時產生過多的線程,否則會導致操作系統假死 while (Thread.activeCount() > 20); } }
這段代碼有可能產生問題,因為如果在printThread剛好要打印最后一個元素時removeThread恰好刪除了一個元素,則printThread就會產生ArrayIndexOutOfBoundsException
3.相對線程安全
相對的線程安全就是我們通常意義上所講的線程安全,它需要保證對這個對象多帶帶的操作是線程安全的,我們在調用的時候不需要做額外的保障措施,但是對于一些特定順序的連續調用,就可能需要在調用端使用額外的同步手段來保證調用的正確性。如上面的Vector容器就是相對線程安全
4.線程兼容
線程兼容是指對象本身并不是線程安全的,但是可以通過在調用端正確地使用同步手段來保證對象在并發環境中可以安全地使用,我們平常說一個類不是線程安全的,絕大多數時候指的是這一種情況
5.線程對立
指的是指無論調用端是否采取了同步措施,都無法在多線程環境中并發使用的代碼。如Thread的suspend()和resume()
二.實現線程安全的方法
1.互斥同步
互斥是因,同步是果;互斥是方法,同步是目的。
在Java中,最基本的互斥同步手段就是synchronized關鍵字,synchronized關鍵字經過編譯之后,會在同步塊的前后分別形成monitorenter和monitorexit這兩個字節碼指令,這兩個字節碼都需要一個reference類型的參數來指明要鎖定和解鎖的對象。
synchronized同步塊對同一條線程來說是可重入的,不會出現自己把自己鎖死的問題。
2.非阻塞同步
互斥同步也叫阻塞同步。阻塞同步屬于一種悲觀的并發策略,在有可能出現并發問題的地方都要進行同步措施(如加鎖)。非阻塞同步采用樂觀的并發策略,通過沖突檢測檢查是否有競爭,有的進行補償措施(如不斷重試直到成功),因為他不用把線程掛起所以是非阻塞的。可是這就好了嗎,要知道我們肯定要保證沖突檢測和操作這兩個操作的原子性啊,那怎么保證呢,肯定不能用互斥同步(這樣的話還是變成會阻塞的),答案是必須要有硬件指令集的支持來保證原子性。
這類常用指令有:
測試并設置(Test-and-Set)。
獲取并增加(Fetch-and-Increment)。
交換(Swap)。
比較并交換(Compare-and-Swap,下文稱CAS)。
加載鏈接/條件存儲(Load-Linked/Store-Conditional,下文稱LL/SC)。
Java中的CAS操作由sun.misc.Unsafe類里面的compareAndSwapInt()和compareAndSwapLong()等幾個方法包裝提供,虛擬機在內部對這些方法做了特殊處理,即時編譯出來的結果就是一條平臺相關的處理器CAS指令,沒有方法調用的過程,或者可以認為是無條件內聯進去了。
由于Unsafe類不是提供給用戶程序調用的類(Unsafe.getUnsafe()的代碼中限制了只有啟動類加載器(Bootstrap ClassLoader)加載的Class才能訪問它),因此,如果不采用反射手段,我們只能通過其他的JavaAPI來間接使用它,如J.U.C包里面的整數原子類,其中的compareAndSet()和getAndIncrement()等方法都使用了Unsafe類的CAS操作。
無同步方案
有些代碼不涉及共享數據,當然就不需要同步了。
三.鎖優化
1.自旋鎖與自適應自旋
自旋就是獲取鎖失敗的時候讓線程忙循環,而不是阻塞,這可以提高響應速度以及避免線程切換的開銷,但浪費CPU時間。
自適應自旋就是如果循環獲取一定次數還沒獲取成功,就阻塞。
自旋鎖在JDK中默認關閉,可使用用-XX:+UseSpinning開啟
2.鎖消除
鎖消除是指虛擬機即時編譯器在運行時,對一些代碼上要求同步,但是被檢測到不可能存在共享數據競爭的鎖進行消除。鎖消除的主要判定依據來源于逃逸分析的數據支持,如果判斷在一段代碼中,堆上的所有數據都不會逃逸出去從章已經講解過逃逸分析技術),如果判斷在一段代碼中,堆上的所有數據都不會逃逸出去從而被其他線程訪問到,那就可以把它們當做棧上數據對待,認為它們是線程私有的,同步加鎖自然就無須進行。
3.鎖粗化
防止過于頻繁的加鎖解鎖操作
4.輕量級鎖
在代碼進入同步塊的時候,如果此同步對象沒有被鎖定(鎖標志位為“01”狀態),虛擬機首先將在當前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲鎖對象目前的Mark Word的拷貝(官方叫Displaced Mark Word)。
5.偏向鎖
偏向鎖優化點加解鎖在于連CAS操作都不用了
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67907.html
摘要:年開始工作,年畢業,兩年來的工作接觸知識面很廣,用的東西比較多,包括基礎的開發到開發到大數據,推薦系統,到服務器運維,到數據庫維護,,,可愈發明白貪多嚼不爛的道理,唯有才能踏踏實實,趁著剛剛讀完這本書,想復習,順便寫一些筆記,聊以鞏固。 13年開始工作,14年畢業,兩年來的工作接觸知識面很廣,用的東西比較多,包括基礎的java開發到j2ee,web開發,到大數據,推薦系統,到服務器運維...
摘要:前言了解中的對象變量等存放的內存區域十分重要本文將全面講解虛擬機中的內存模型分區,希望你們會喜歡目錄張圖解就能讓女朋友徹底了解中的內存模型,快上車虛擬機 前言了解Java中的對象、變量等存放的內存區域十分重要本文將全面講解Java虛擬機中的內存模型 & 分區,希望你們會喜歡目錄1、內存模型 & 分區Java虛擬機在運行Ja...
摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...
摘要:開頭正式開啟我入職的里程,現在已是工作了一個星期了,這個星期算是我入職的過渡期,算是知道了學校生活和工作的差距了,總之,盡快習慣這種生活吧。當時是看的廖雪峰的博客自己也用做爬蟲寫過幾篇博客,不過有些是在前人的基礎上寫的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 開頭 2017.08.21 正式開啟我...
閱讀 1138·2021-10-27 14:13
閱讀 2642·2021-10-09 09:54
閱讀 906·2021-09-30 09:46
閱讀 2429·2021-07-30 15:30
閱讀 2172·2019-08-30 15:55
閱讀 3415·2019-08-30 15:54
閱讀 2857·2019-08-29 14:14
閱讀 2778·2019-08-29 13:12