【高并發】線程安全性
大家好,我是冰河~~
概念
定義:當多個線程訪問某個類時,不管運行時環境采用何種調度方式或者這些進程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么就稱這個類是線程安全的。
原子性:提供了互斥訪問,同一時刻只能有一個線程來對它進行操作。
可見性:一個線程對主內存的修改可以及時的被其他線程觀察到。
有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序的存在,該觀察結果一般雜亂無序。
原子性
原子性-Atomic包
AtomicXXX:CAS、Unsafe.compareAndSwapInt
AtomicLong、LongAdder
AtomicReference、AtomicReferenceFieldUpdater
AtomicStampReference:解決CAS的ABA問題
原子性-鎖
synchronized:依賴JVM
Lock:依賴特殊的CPU指令,代碼實現,ReentrantLock
原子性-synchronized
修飾代碼塊:大括號括起來的代碼,作用于調用的對象。
修飾方法:整個方法,作用于調用的對象。
修飾靜態方法:整個靜態方法,作用于所有對象。
修飾類:括號括起來的部分,作用于所有對象。
原子性-對比
synchronized:不可中斷鎖,適合競爭不激烈,可讀性好
Lock: 可中斷鎖,多樣化同步,競爭激烈時能維持常態
Atomic: 競爭激烈時能維持常態,比Lock性能好,只能同步一個值
可見性
導致共享變量在線程間不可見的原因
(1)線程交叉執行
(2)重排序結合線程交叉執行
(3)共享變量更新后的值沒有在工作內存與主存間及時更新
可見性-synchronized
JMM(Java內存模型)關于synchronized的兩條規定:
(1)線程解鎖前,必須把共享變量的最新值刷新到主內存
(2)線程加鎖前,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值(注意:加鎖和解鎖是同一把鎖)
可見性-volatile
通過加入內存屏障和禁止重排序優化來實現
(1)對volatile變量寫操作時,會在寫操作后加入一條store屏障指令,將本地內存中的共享變量值刷新到主內存
(2)對volatile變量讀操作時,會在讀操作前加入一條load屏障指令,從主內存中讀取共享變量
注意:volatile不具有原子性
volatile的使用場景
(1)對變量的寫操作不依賴于當前值
(2)該變量沒有包含在具有其他變量的不必要的式子中
有序性
Java內存模型中,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單線程程序的執行,卻會影響到多線程并發執行的正確性
可以通過volatile、synchronized、Lock保證有序性
有序性-happens-before原則
(1)程序次序規則:一個線程內,按照代碼順序,書寫在前面的操作先行發生于書寫在后面的操作
(2)鎖定規則:一個unLock操作先行發生于后面對同一個鎖的lock操作
(3)volatile變量規則:對一個變量的寫操作先行發生于后面對這個變量的讀操作
(4)傳遞規則:如果操作A先行發生于操作B,而操作B又先行發生于操作C,則可以得出操作A先行發生于操作C
(5)線程啟動規則:Thread對象的start()方法先行發生于此線程的每一個動作
(6)線程中斷規則:對線程interrupt()方法的調用先行發生于被中斷線程的代碼檢測到中斷事件的發生
(7)線程終結規則:線程中所有的操作都先行發生于線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行
(8)對象終結規則:一個對象的初始化完成先行發生于它的finalize()方法的開始
注意:如果兩個操作的執行次序無法從happens-before原則推導出來,那么它們就無法保證有序性,虛擬機就可以隨意地對它們進行重排序。
好了,今天就到這兒吧,我是冰河,我們下期見~~
Java JVM 任務調度 多線程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。