線程安全

對于下面這段代碼袱耽,輸出是什么?

package com.conrrentcy.thread;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadSafeProblem {

    private final static Object locker = new Object();
    private static int count = 0;
    private static final Logger log = LoggerFactory
            .getLogger(ThreadSafeProblem.class);

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {

            for (int i = 0; i < 5000; i++) {
                count++;
            }

        }, "t1");

        Thread t2 = new Thread(() -> {

            for (int i = 0; i < 5000; i++) {
                count --;
            }

        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        log.info(" count  value is  {}", count);

    }

}

以上的結(jié)果可能是整數(shù)也可能是負數(shù)也可能是0据途。因為java對靜態(tài)變量的自增绞愚、自減并不是原子性的啄巧。要徹底理解巫击,必須從字節(jié)碼角度來看钠怯。
對于靜態(tài)count++ 而言 敦锌,會產(chǎn)生如下JVM的指令

getstatic count //獲取靜態(tài)變量的值
iconst_1  //準備常量1
iadd      //自增
putstatic count // 將修改后的在值放入count

對于靜態(tài)count-- 而言 痢站,會產(chǎn)生如下JVM的指令

getstatic count //獲取靜態(tài)變量的值
iconst_1  //準備常量1
isub      //自減
putstatic count // 將修改后的在值放入count

java內(nèi)存模型中婆排,主存的每個線程的內(nèi)存需要同步霉猛,主存是真正進行運算的地方钻蹬, 主存運算完的值需要同步回線程內(nèi)存佛致。 在多個線程的情況下贮缕,如果主存的值可以被多個線程修改,那么當 同步回去時晌杰,就有極大可能和單獨一個線程修改的值不同跷睦。這就產(chǎn)生了線程安全問題。

image.png

如果指令按照線程順序運行肋演,就不會有任何問題

image.png

如果出現(xiàn)交錯抑诸,就可能有兩種情況

  • 負數(shù)


    image.png
  • 正數(shù)


    image.png

臨界區(qū)

多線程的的問題主要是對共享資源發(fā)生讀寫指令交錯,就會出現(xiàn)問題爹殊。
一個代碼塊如果存在對共享資源的多線程讀寫操作蜕乡,稱之為臨界區(qū)。

競態(tài)條件

多個線程在臨界區(qū)運行梗夸,由于代碼執(zhí)行序列導(dǎo)致結(jié)果無法預(yù)測层玲,稱之為競態(tài)條件。

互斥

避免競態(tài)條件的發(fā)生反症,可以用以下方式:

  • 阻塞式 synchronize lock
  • 非阻塞式 原子變量

這章主要介紹synchronize 關(guān)鍵字的阻塞方法辛块,就是對象鎖。

package com.conrrentcy.thread;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadSafeProblem {

    private final static Object locker = new Object();
    private static int count = 0;
    private static final Logger log = LoggerFactory
            .getLogger(ThreadSafeProblem.class);

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {

            for (int i = 0; i < 5000; i++) {
                synchronized (locker) {
                    count++;
                }
            }

        }, "t1");

        Thread t2 = new Thread(() -> {

            for (int i = 0; i < 5000; i++) {
                synchronized (locker) {
                    count--;
                }
            }

        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        log.info(" count  value is  {}", count);

    }

}

下面代碼铅碍,運行結(jié)果每次都為0润绵。


image.png

思考

  • 如果synchronized 加在 for 循環(huán)外面
  • 如果t1 synchronized object1 , t2 synchronized object2
  • 如果t1 synchronized, t2不加

這三種情況各是什么行為?
synchronized 加在對象 和 方法上有什么區(qū)別胞谈?

變量的線程安全分析

成員變量和靜態(tài)變量

  • 如果他們沒有共享尘盼,則線程安全
  • 如果共享并且只有讀操作憨愉,線程安全。
  • 如果共享有讀寫操作卿捎,則需要考慮線程安全配紫。(有可能只有寫操作么?)
package com.conrrentcy.thread;

import java.util.ArrayList;
import java.util.List;

public class ThreadSafeReference {
    public static void main(String[] args) throws InterruptedException {
        ThreadUnSafeList tl = new ThreadUnSafeList();   
        for(int i=0; i<2;i++){
            
            new Thread(()->{
                tl.method1(200);
            },"t"+i).start();
        }
        
        ThreadSafeList tl2 =  new ThreadSafeList();
        for(int i=0;i <2;i++){
            new Thread(()->{
                tl2.method1(200);
            },"t"+i).start();
        }
    }
 
}

class ThreadUnSafeList {
     private List  list = new ArrayList();
     
     public void method1(int loopNumber){
         for( int i=0; i<loopNumber;i++){
             method2();
             method3();
         }
     }
     private void method2(){
         list.add("1");
     }
     
     private void method3(){
         list.remove(0);
     }
     
}

class ThreadSafeList {
     
     
     public void method1(int loopNumber){
         List  list = new ArrayList();
         for( int i=0; i<loopNumber;i++){
             method2(list);
             method3(list);
         }
     }
     private void method2(List list){
         list.add("1");
     }
     
     private void method3(List list){
         list.remove(0);
     }
     
}

比較一下ThreadUnSafeList 和 ThreadSafeList 有啥不同午阵?下面是ThreadUnSafeList 的內(nèi)存模型


image.png

局部變量

  • 局部變量是線程安全的
  • 局部變量引用的對象如果沒有逃離方法的作用域躺孝,線程安全
  • 局部變量引用的對象超出方法的作用域,需要考慮線程安全底桂。
  • 局部變量在棧幀中重建多份括细,不存在共享
public  static void test(){
    int  i=0;
    i++;
}

image.png

下面是剛才代碼ThreadSafeList 的內(nèi)存模型

image.png

但是,光是局部變量也不能完全保證線程安全戚啥,需要保證在同一個線程內(nèi)部沒有起另外一個線程, 下面的代碼就不是線程安全的锉试,主要是在method3猫十, 又另外起了一個線程。導(dǎo)致list 雖然對是一個local 變量呆盖,但是在method3拖云, 內(nèi)部就不再是一個線程變量了。

package com.conrrentcy.thread;

import java.util.ArrayList;
import java.util.List;

public class ThreadExpose {

    public static void main(String[] args) {
        ThreadSafeList2 tlc= new ThreadSafeListChild();
        for(int i=0;i <2;i++){
            new Thread(()->{
                tlc.method1(200);
            },"t"+i).start();
        }

    }
    

}
class ThreadSafeList2 {
     
     
     public void method1(int loopNumber){
         List  list = new ArrayList();
         for( int i=0; i<loopNumber;i++){
             method2(list);
             method3(list);
         }
     }
     public void method2(List list){
         list.add("1");
     }
     
     public void method3(List list){
         list.remove(0);
     }
     
}

class ThreadSafeListChild extends ThreadSafeList2{
     public void method3(List list){
         new Thread(()->{
         list.remove(0);
         },"tchild").start();
     }
}

class modifier - private, final 能夠加強線程安全

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末应又,一起剝皮案震驚了整個濱河市宙项,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌株扛,老刑警劉巖尤筐,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洞就,居然都是意外死亡盆繁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門旬蟋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來油昂,“玉大人,你說我怎么就攤上這事倾贰∶岬” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵匆浙,是天一觀的道長安寺。 經(jīng)常有香客問我,道長吞彤,這世上最難降的妖魔是什么我衬? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任叹放,我火速辦了婚禮,結(jié)果婚禮上挠羔,老公的妹妹穿的比我還像新娘井仰。我一直安慰自己,他們只是感情好破加,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布俱恶。 她就那樣靜靜地躺著,像睡著了一般范舀。 火紅的嫁衣襯著肌膚如雪合是。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天锭环,我揣著相機與錄音聪全,去河邊找鬼。 笑死辅辩,一個胖子當著我的面吹牛难礼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播玫锋,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛾茉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了撩鹿?” 一聲冷哼從身側(cè)響起谦炬,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎节沦,沒想到半個月后键思,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡甫贯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年稚机,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片获搏。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赖条,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出常熙,到底是詐尸還是另有隱情纬乍,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布裸卫,位于F島的核電站仿贬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏墓贿。R本人自食惡果不足惜茧泪,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一蜓氨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧队伟,春花似錦穴吹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锈颗,卻和暖如春顷霹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背击吱。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工淋淀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人覆醇。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓绅喉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叫乌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 什么是線程安全徽缚? 為什么有線程安全問題憨奸? 當多個線程同時共享,同一個全局變量或靜態(tài)變量凿试,做寫的操作時排宰,可能會發(fā)生數(shù)...
    okhoogh閱讀 423評論 0 0
  • 1.并發(fā)基礎(chǔ) 定義:一個cpu“同時”處理多個任務(wù),而多個線程都在爭取這個cpu資源 1.1 優(yōu)點 充分發(fā)揮多核C...
    smartzheng閱讀 984評論 0 0
  • 1.并發(fā)基礎(chǔ) 定義:一個cpu“同時”處理多個任務(wù)那婉,而多個線程都在爭取這個cpu資源 1.1 優(yōu)點 充分發(fā)揮多核C...
    程序人生a閱讀 356評論 0 0
  • 為什么CFRunLoopRef是線程安全的板甘,而基于此的NSRunLoop卻不是線程安全的呢? 線程安全時多線程領(lǐng)域...
    小貓仔閱讀 1,158評論 2 4
  • 前言:上一節(jié)學(xué)習(xí)了JMM详炬、Happen Before盐类、可見性等等這種概念,基本都是來源于JDK的官方網(wǎng)站中呛谜,上面有...
    Vander1991閱讀 417評論 0 0