Java中線程安全問題
一.線程不安全
多線程的執(zhí)行環(huán)境中,程序的執(zhí)行結果和預期的結果不符合,這就稱為發(fā)生了線程不安全現(xiàn)象
二.那些情況導致了線程不安全?
大致分為以下5種情況:
(1)CPU搶占執(zhí)行 (無法解決);
(2)非原子性 ;
(3)編譯器優(yōu)化(指令重排) 編譯器優(yōu)化在單線程下執(zhí)行沒問題,多線程下優(yōu)化會發(fā)生混亂;
(4)內(nèi)存的不可見性 ;(volatile輕量級解決)
(5)多個線程修改了同一個變量。(方案:讓線程操作自己的變量可以解決該問題,但業(yè)務場景發(fā)生變化,修改難度變大,通用性不高)
三.Java中解決線程不安全的方案
1.volatile“輕量級”解決線程不安全

volatile的出現(xiàn)可以解決上圖所展現(xiàn)的內(nèi)存不可見問題以及禁止指令重排
實現(xiàn)原理:工作內(nèi)存中的變量操作結束后,強制刪除線程工作內(nèi)存中的變量,起到內(nèi)存可見注意事項:volatile不能解決原子性問題。volatile可以解決線程不安全問題是錯誤的(說法不夠嚴謹)
下面兩種方案通過對關鍵代碼加鎖,讓cpu排隊執(zhí)行,鎖操作的步驟為:
1)嘗試獲取鎖,如果拿到則加鎖,否則排隊等待獲取鎖 2)釋放鎖操作
2.synchronized自動加鎖
①synchronized進行自動加鎖和釋放鎖,是Jvm層面的解決方案
synchronized使用舉例:使用兩個線程對變量count進行一次++ 和 一次- -

沒有使用synchronized上鎖之前,由于非原子問題,兩個線程進行++和- -出現(xiàn)線程不安全問題,通過synchronized關鍵字的使用,解決了非原子問題,代碼運行實際結果和預期結果一致,保證了線程安全。
②synchronized實現(xiàn)原理:
1.基于操作系統(tǒng)而言,通過互斥鎖mutex實現(xiàn)
mutex結構信息:
2.從Jvm層面來看,實現(xiàn)了一個監(jiān)視器鎖的加鎖和釋放鎖過程。
3.從Java語言本身來看,存在一個互斥鎖mutex對象,鎖存在于對象的對象頭中,對象頭中的“偏向線程ID”,表明該鎖被該線程占有,釋放鎖后,偏向線程ID消失。
Owner代表鎖的擁有者,為null時表示鎖未使用;Nest表示鎖的使用次數(shù),為0表示沒有被使用;此外鎖可以嵌套使用,不會發(fā)生死鎖情況。
③synchronized鎖升級過程:
沒有線程訪問時處于無鎖狀態(tài) >> 第一個線程訪問時,由無鎖狀態(tài)轉(zhuǎn)為偏向鎖 >> 輕量級鎖(其他線程嘗試獲取鎖,鎖處于自旋狀態(tài)) >> 重量級鎖(把沒有拿到鎖的線程放到等待隊列里面)
3.Lock手動上鎖
Lock需要程序員自己手動上鎖手動釋放鎖;Lock是一個interface;創(chuàng)建鎖時可以通過Lock的實現(xiàn)類ReentrantLock()完成:Lock lock = new ReentrantLock();加鎖操作lock.lock(),釋放鎖操作lock.unlock()
使用Lock需要注意的問題:
一定要把加鎖操作lock()放在try/finally外面如果把lock()放在try中會導致兩個問題發(fā)生:
(1)try中代碼出現(xiàn)異常,此時就會執(zhí)行finally中釋放鎖的操作,如果try還沒有加鎖就去釋放鎖,勢必是不行的。
(2)try中出現(xiàn)異常后,執(zhí)行finally中釋放鎖操作,線程狀態(tài)異常會將try中業(yè)務異常覆蓋掉,增加了排除錯誤的成本。
將lock()放在try中第一句可以解決這個問題
對比發(fā)現(xiàn),這樣做業(yè)務異常是不會被線程的狀態(tài)異常覆蓋的,方便了排查錯誤?。?!
四.公平鎖與非公平鎖機制
公平鎖線程按順序執(zhí)行;非公平鎖沒有順序,執(zhí)行效率更高;Java中默認鎖策略為非公平鎖機制synchronized鎖機制:采用非公平鎖機制Lock鎖機制:默認采用非公平鎖機制,但是可以顯示地聲明為公平鎖,比如在創(chuàng)建鎖對象時,在構造方法中傳true:Lock lock = new ReentrantLock(true)
五.volatile和synchronized的區(qū)別
volatile可以解決內(nèi)存不可見問題以及禁止指令重排序,但是不能解決非原子性問題;
synchronized可以解決大部分線程的非安全問題,保證關鍵代碼排隊執(zhí)行,無論何時鎖只被一個線程擁有,可解決非原子性問題
六.synchronized和Lock的區(qū)別
1.synchronized自動加鎖和釋放鎖,而Lock需要手動加鎖和釋放鎖;
2.synchronized是Jvm層面的實現(xiàn),Lock是Java語言層面的實現(xiàn);
3.適用范圍不同:synchronized可以修飾代碼塊(對任意對象加鎖)、修飾靜態(tài)方法(對當前的類進行加鎖)、修飾普通的方法(對當前的實例對象進行加鎖);而Lock只能修飾代碼塊;
4.synchronized只有非公平鎖策略;Lock默認采用非公平鎖機制,但可以顯示聲明為公平鎖;
5.Lock的靈活性更高一些(比如:tryLock)
到此這篇關于Java中線程安全問題的文章就介紹到這了,更多相關Java線程安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
FastJson對于JSON格式字符串、JSON對象及JavaBean之間的相互轉(zhuǎn)換操作
這篇文章主要介紹了FastJson對于JSON格式字符串、JSON對象及JavaBean之間的相互轉(zhuǎn)換,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-11-11
利用IDEA社區(qū)版創(chuàng)建SpringBoot項目的詳細圖文教程
大家應該都知道Idea社區(qū)版本,默認是不能創(chuàng)建SpringBoot項目的,下面這篇文章主要給大家介紹了關于利用IDEA社區(qū)版創(chuàng)建SpringBoot項目的詳細圖文教程,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2023-04-04
Java8 CompletableFuture 異步執(zhí)行操作
CompletableFuture是java8提供的基于異步操作的封裝,日常開發(fā)中經(jīng)常會用到,接下來通過本文給大家介紹Java8 CompletableFuture 異步執(zhí)行操作,感興趣的朋友一起看看吧2021-06-06
Springcloud基于OpenFeign實現(xiàn)服務調(diào)用代碼實例
這篇文章主要介紹了Springcloud基于OpenFeign實現(xiàn)服務調(diào)用代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
java ConcurrentHashMap分段加鎖提高并發(fā)效率
這篇文章主要為大家介紹了java ConcurrentHashMap分段加鎖提高并發(fā)效率,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12







