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