Java JUC系列目錄連結
Java 線程池核心原了解析
- 何為線程安全
- 如何解決線程安全
- 結語
何為線程安全
直接上個僞代碼:
boolean a = 多個線程共享變量;
boolean b = 存在寫操作;
boolean c = 寫操作導緻資料沖突;
if( a && b && c ) {
存線上程安全問題;
}
文字描述就是,隻有
多個線程共享變量
,
線程存在寫操作
,
操作導緻資料沖突
同時滿足的情況下,才會存線上程安全問題。
如何解決線程安全
解決線程安全還不簡單,不就是讓上面的if的條件不為真嘛!顯而易見,隻要a,b,c中有一個或多個恒為false,就不會出現線程安全問題。
下面我們就給出具體解決方案:
-
‘a’ always == false:
即:多個線程不共享變量。在這之前我們先來想一下,為什麼多個線程會共享變量?聯系Java虛拟機模型,我們知道,如果我們的變量是存在堆區的,那麼這個變量就會被多個線程所共享。明白了這一點就好辦了,
*方案1
我們直接把變量定義到棧,即局部變量,那不就不會跟其他線程共享啦,自然舊解決了線程安全的問題。
僞代碼如下:
class A { // int a = 1; public void test(){ int a = 1; } }
我們把原來的全局變量a移到了局部變量,這樣a就會被存放到方法’test’的方法棧中,自然就不會有線程安全的問題了。
如果說你非要犟,我就是要把變量存到堆中去,也要不讓線程之間共享變量,那怎麼辦?
别慌,我們還是有辦法的,如果大家聽過ThreadLocal這個類,那一定不會束手無策了。
ThreadLocal為每一個線程需要的變量提供一個本地的副本,這樣就不會有線程安全問題啦。什麼意思?簡單來說就是,你線程不是想要a變量嘛,我給你們線程沒人一個a變量,但是你們都隻能各玩各的,不許去玩别人的。但是使用ThreadLocal有一點要注意,你建立的這個對象如果存放在堆中的話,還是可能會導緻線程安全問題的。*方案2
-
‘b’ always == false:
即:線程不存在寫操作。既然你說寫操作會有線程安全問題,那我們來個絕的,既然寫了問題這麼多,大家都别想寫了!
直接把變量變成final類型,直接上僞代碼:*方案3
雖然這個解決方案有點絕,但也不失為一總可選方案呀。class A { final int a = 1; }
-
‘c’ always == false:
即:寫操作不導緻資料沖突。寫操作不沖突?emm…讓我想想,既然你們都一起來搞會有沖突,那這樣,你們排隊一個個來玩,那不就完了嘛。這就是我們最熟悉的——‘鎖’。
*方案4
我們使用鎖來確定線程依次對變量進行操作,這樣就避免了線程安全問題,至于怎麼加鎖synchronized,Lock啥的随便你選,我就不說了。
你說什麼?加鎖性能低?沒辦法了,隻能掏出這個了——‘CAS’,即為樂觀鎖。想要確定線程的寫操作不導緻資料沖突,加鎖确實是性能較低的一種解決方案,樂觀鎖看起來名字像是鎖,其實是一種無鎖機制,原理想必你們背面試題肯定是十分熟練了。順便一提的是,傳統CAS可能會導緻‘ABA’問題,是以有人在樂觀鎖的實作中加入了時間戳(版本号)。
結語
本文隻是從廣度上簡介了關于線程安全的一些問題,關于文中提到的一些深度問題,比如jvm模型,ThreadLocal啥的與本文無關,這裡就不再贅述了。