天天看點

Java juc系列7 —— 線程安全何為線程安全如何解決線程安全結語

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這個類,那一定不會束手無策了。

    *方案2

    ThreadLocal為每一個線程需要的變量提供一個本地的副本,這樣就不會有線程安全問題啦。什麼意思?簡單來說就是,你線程不是想要a變量嘛,我給你們線程沒人一個a變量,但是你們都隻能各玩各的,不許去玩别人的。但是使用ThreadLocal有一點要注意,你建立的這個對象如果存放在堆中的話,還是可能會導緻線程安全問題的。
  • ‘b’ always == false:

    即:線程不存在寫操作。既然你說寫操作會有線程安全問題,那我們來個絕的,既然寫了問題這麼多,大家都别想寫了!

    *方案3

    直接把變量變成final類型,直接上僞代碼:
    class A {
    	final int a = 1;
    	}
               
    雖然這個解決方案有點絕,但也不失為一總可選方案呀。
  • ‘c’ always == false:

    即:寫操作不導緻資料沖突。寫操作不沖突?emm…讓我想想,既然你們都一起來搞會有沖突,那這樣,你們排隊一個個來玩,那不就完了嘛。這就是我們最熟悉的——‘鎖’。

    *方案4

    我們使用鎖來確定線程依次對變量進行操作,這樣就避免了線程安全問題,至于怎麼加鎖synchronized,Lock啥的随便你選,我就不說了。

    你說什麼?加鎖性能低?沒辦法了,隻能掏出這個了——‘CAS’,即為樂觀鎖。想要確定線程的寫操作不導緻資料沖突,加鎖确實是性能較低的一種解決方案,樂觀鎖看起來名字像是鎖,其實是一種無鎖機制,原理想必你們背面試題肯定是十分熟練了。順便一提的是,傳統CAS可能會導緻‘ABA’問題,是以有人在樂觀鎖的實作中加入了時間戳(版本号)。

結語

本文隻是從廣度上簡介了關于線程安全的一些問題,關于文中提到的一些深度問題,比如jvm模型,ThreadLocal啥的與本文無關,這裡就不再贅述了。