在 Java 的線程安全是老生常談的問題。經常是各種寫法說法一大堆,感覺很多的來源都是在面試的時候,很多考官都喜歡問線程安全的問題。
起源
這個問題的起源就是 Java 是支持多線程的。如果對進程和線程是什么不太清楚的話,可以惡補下大學課程《操作系統》。
一般來說,JVM 是會以一個進程來運行,當進程啟動后,會啟動多個線程來提高 CPU 的利用率。
如果是多線程的話,那會在訪問同一個變量,同一個代碼的的時候出現數據不同步的情況。
簡單來說對于一個變量 V,如果線程 TA 反問了,并且修改了,在這個時候,TB 線程也訪問變量 V 也進行了修改。
在線程 TB 進行訪問的和計算的時候,TB 不知道 TA 已經對 變量 V 進行了修改,會導致結果計算不準確。這是因為 TB 在拿到變量 V 的時候和 TA 在拿到變量 V 的時候的數據是不一樣的。
這個就是線程安全的問題來源:線程同時訪問了一個變量或者代碼塊。
思路
如果只是針對上面的思路的話,我們不定義全部變量 V 不就沒有問題了。
事實上也是這樣的,如果你定義的變量是線程內的變量,或者不可以修改的變量的話,是沒有多線程的問題的。
因此考慮線程安全的問題,就是要考慮你定義的變量或者方法,在多個線程進行訪問的和計算的時候會不會有不同的結果。
如果沒有不同結果:線程安全。
有可能會得到不同的結果:線程不安全。
基于上面的說法,我們有多個辦法可以參考下:
無狀態實現( stateless implementations):沒有使用全局變量,所有的變量都是方法內的變量。
不可變實現( Immutable Implementations):對象在創建后就不能被修改了。考察下 String 定義。
線程安全類(thread-safe classes):類中的所有變量都會在本線程中使用,這個變量是不會與其他線程共享的,例如: private final 的 List。
同步( Synchronized):方法或者類或狀態的同步,也可實現線程安全。
鎖(Lock):對鎖的使用。
其實還有多種其他的方法來實現線程安全。
實際上在對 Java 的開發中,需要對線程安全的概念有所了解,并且知道查看 API 的時候需要了解會不會有線程安全的問題即可。
在實際的開發中,很多人就直接使用 Synchronized 關鍵字來實現方法的線程安全了。因為這個實現是最簡單的。
可以看看 StringBuffer 的源代碼也就會有所了解了。