Java保證線程安全的方式有哪些?
? 一位工作5年的小伙伴面試時被問到這樣一道題,說Java保證線程安全的方式有哪些?
今天,我給大家分享一下我的理解。
1、線程不安全的原因
回答這個問題之前,得先了解導(dǎo)致對象線程不安全的原因,主要有三個:
- 原子性:一個或者多個操作在CPU執(zhí)行過程中被中斷。
- 可見性:一個線程對象共享變量的修改,導(dǎo)致另一個線程不能立即看到。
- 有序性:程序執(zhí)行的順序沒有按照代碼的先后順序執(zhí)行。
原子性和可見性比較容易理解,重點分析一下有序性。為什么程序執(zhí)行的順序會和代碼的編寫順序不一致呢?這就得理解Java平臺的兩種編譯器,靜態(tài)編譯器javac和動態(tài)編譯器jit(just in time)。
靜態(tài)編譯器是將.java文件編譯成.class文件,JVM加載后就可以執(zhí)行了。
而動態(tài)編譯器是要將.class文件編譯成機器碼,再由JVM執(zhí)行。有時候,動態(tài)編譯器為了程序的整體性能會對指令進行重排序,但是,這又會導(dǎo)致源代碼中指定的內(nèi)存訪問順序和實際的執(zhí)行順序不一致,就會出現(xiàn)線程不安全的問題。
?2、如何保證線程安全
那么,針對以上三種情況,如何保證對象的線程安全呢?
第1個,針對原子性。
(1)JDK提供了非常多的Atomic類,比如AtomicInteger、AtomicLong、AtomicBoolean等等。這些類都是通過CAS來保證原子性。
(2)另外,Java還提供了各種鎖機制,來保證鎖內(nèi)的代碼塊在同一時刻只能被一個線程執(zhí)行。比如用synchronized加鎖。這樣,就可以保證一個線程對資源進行讀、寫操作時,其他線程不可以對這個資源進行操作,從而保證了線程安全。?
第2個,針對可見性。
同樣可以使用synchronized關(guān)鍵字加鎖來解決。與此同時,Java提供了volatile關(guān)鍵字。它要優(yōu)于synchronized的性能,同樣也可以保證修改后對其他線程可見。volatile一般用于對變量的寫操作,不依賴于當前值的場景中,比如狀態(tài)標記量等等。
第3個,針對有序性。
也可以使用synchronized關(guān)鍵字定義同步代碼塊,或者同步方法來保證有序性。另外也可以通過Lock接口來保證有序性。
以上就是對Java保證線程安全的思路。當然,保證對象線程安全的方式還有很多,比如還可以使用ThreadLocal實現(xiàn)多個線程之間的數(shù)據(jù)隔離,使用final關(guān)鍵字等等,我這里就不一一列舉了。最后,我留一個思考題,單用volatile關(guān)鍵字,能保證線程安全嗎?