摘要:另一個是使用鎖的機制來處理線程之間的原子性。依賴于去實現鎖,因此在這個關鍵字作用對象的作用范圍內,都是同一時刻只能有一個線程對其進行操作的。
線程安全性
定義:當多個線程訪問某個類時,不管運行時環境采用何種調度方式或者這些線程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么就稱這個類是線程安全的。
線程安全性主要體現在三個方面:原子性、可見性、有序性:
原子性:提供了互斥訪問,同一時刻只能有一個線程來對它進行操作
可見性:一個線程對主內存的修改可以及時地被其他線程觀察到
有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序的存在,該觀察結果一般雜亂無序
原子性原子性在 JDK 中主要由兩個方面體現出來:
Atomic一個是 JDK 中已經提供好的 Atomic 包,它們均使用了 CAS 完成線程的原子性操作(詳見【Java并發】淺析 AtomicLong & LongAdder)。
另一個是使用鎖的機制來處理線程之間的原子性。鎖主要包括:synchronized、lock。
synchronized依賴于 JVM 去實現鎖,因此在這個關鍵字作用對象的作用范圍內,都是同一時刻只能有一個線程對其進行操作的。synchronized 是 Java 中的一個關鍵字,是一種同步鎖。它可以修飾的對象主要有四種:
修飾代碼塊:大括號括起來的代碼,作用于調用的對象
修飾方法:整個方法,作用于調用的對象
修飾靜態方法:整個靜態方法,作用于所有對象
修飾類:括號括起來的部分,作用于所有對象
注意:如果當前類是一個父類,子類調用父類的被 synchronized 修飾的方法,不會攜帶 synchronized 屬性,因為 synchronized 不屬于方法聲明的一部分。Lock
首先要說明的就是 Lock,通過查看 Lock 的源碼可知,Lock 是一個接口。ReentrantLock 是唯一實現了 Lock 接口的類,意思是“可重入鎖”,并且 ReentrantLock 提供了更多的方法。
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
可見性鎖的分類
可重入鎖
synchronized / ReentrantLock
可中斷鎖
synchronized 不可中斷,Lock 可中斷
公平鎖
synchronized 非公平鎖
ReentrantLock 和 ReentrantReadWriteLock 默認情況下非公平鎖,可設置為公平鎖
讀寫鎖
ReadWriteLock / ReentrantReadWriteLock
導致共享變量在線程間不可見的原因:
線程交叉執行
重排序結合線程交叉執行
共享變量更新后的值沒有在工作內存與主存間及時更新
JVM 對于可見性,提供了 synchronized 和 volatile:
synchronizedJMM 關于 synchronized 的兩條規定:
線程解鎖前,必須把共享變量的最新值刷新到主內存
線程加鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值(注意:加鎖與解鎖是同一把鎖)
volatilevolatile 的方式是:通過加入內存屏障和禁止重排序優化來實現。
對 volatile 變量寫操作時,會在寫操作后加入一條 store 屏障指令,將本地內存中的共享變量值刷新到主內存。
對 volatile 變量讀操作時,會在讀操作前加入一條 load 屏障指令,從主內存中讀取共享變量。
volatile的屏障操作都是 cpu 級別的;適合狀態驗證,不適合累加值,volatile關鍵字不具有原子性。
適合狀態驗證,不適合累加值,volatile關鍵字不具有原子性
有序性Java 內存模型中,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單線程程序的執行,卻會影響到多線程并發執行的正確性。而 Java 提供了 volatile、synchronized、Lock,它們可以用來保證有序性。
另外,Java 內存模型具備一些先天的有序性,即不需要任何手段就能得到保證的有序性。通常被我們稱為happens-before 原則(先行發生原則)。如果兩個線程的執行順序無法從 happens-before 原則推導出來,那么就不能保證它們的有序性,虛擬機就可以對它們進行重排序。
思維導圖【以下規則摘抄自《深入理解Java虛擬機》】
程序次序規則:一個線程內,按照代碼順序,書寫在前面的操作先行發生于書寫在后面的操作
鎖定規則:一個unlock操作先行發生于后面對同一個鎖的lock操作
volatile變量規則:對一個變量的寫操作先行發生于后面對這個變量的讀操作(重要)
傳遞規則:如果操作A先行發生于操作B,而操作B又先行發生于操作C,則可以得出操作A先行發生于操作C
線程啟動規則:Thread對象的start()方法先行發生于此線程的每一個動作
線程中斷規則:對線程interrupt()方法的調用先行發生于被中斷線程的代碼檢測到中斷事件的發生
線程終結規則:線程中所有的操作都先行發生于線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行
筆記整理自:【IMOOC】Java并發編程與高并發解決方案
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73294.html
摘要:并發模塊本身有兩種不同的類型進程和線程,兩個基本的執行單元。調用以啟動新線程。在大多數系統中,時間片發生不可預知的和非確定性的,這意味著線程可能隨時暫停或恢復。 大綱 什么是并發編程?進程,線程和時間片交織和競爭條件線程安全 策略1:監禁 策略2:不可變性 策略3:使用線程安全數據類型 策略4:鎖定和同步 如何做安全論證總結 什么是并發編程? 并發并發性:多個計算同時發生。 在現代...
摘要:同步容器及其注意事項中的容器主要可以分為四個大類,分別是和,但并不是所有的容器都是線程安全的。并發容器及其注意事項在版本之前所謂的線程安全的容器,主要指的就是同步容器,當然因為所有方法都用來保證互斥,串行度太高了,性能太差了。 Java 并發包有很大一部分內容都是關于并發容器的,因此學習和搞懂這部分的內容很有必要。 Java 1.5 之前提供的同步容器雖然也能保證線程安全,但是性能很差...
摘要:本文探討并發中的其它問題線程安全可見性活躍性等等。當閉鎖到達結束狀態時,門打開并允許所有線程通過。在從返回時被叫醒時,線程被放入鎖池,與其他線程競爭重新獲得鎖。 本文探討Java并發中的其它問題:線程安全、可見性、活躍性等等。 在行文之前,我想先推薦以下兩份資料,質量很高:極客學院-Java并發編程讀書筆記-《Java并發編程實戰》 線程安全 《Java并發編程實戰》中提到了太多的術語...
摘要:程序正常運行,輸出了預期容量的大小這是正常運行結果,未發生多線程安全問題,但這是不確定性的,不是每次都會達到正常預期的。另外,像等都有類似多線程安全問題,在多線程并發環境下避免使用這種集合。 這個問題是 Java 程序員面試經常會遇到的吧。 工作一兩年的應該都知道 ArrayList 是線程不安全的,要使用線程安全的就使用 Vector,這也是各種 Java 面試寶典里面所提及的,可能...
閱讀 3688·2021-11-19 09:56
閱讀 1468·2021-09-22 15:11
閱讀 1127·2019-08-30 15:55
閱讀 3371·2019-08-29 14:02
閱讀 2911·2019-08-29 11:07
閱讀 433·2019-08-28 17:52
閱讀 3172·2019-08-26 13:59
閱讀 436·2019-08-26 13:53