線程不安全到底是怎樣的

基本概念

首先看一下線程安全與線程不安全的概念:

線程安全就是多線程訪問時(shí)橡疼,采用了加鎖機(jī)制乔妈,當(dāng)一個(gè)線程訪問該類的某個(gè)數(shù)據(jù)時(shí)妻顶,進(jìn)行保護(hù),其他線程不能進(jìn)行訪問直到該線程讀/寫完匈子,其他線程才可使用锌妻。不會(huì)出現(xiàn)數(shù)據(jù)不一致或者數(shù)據(jù)污染。

線程不安全就是不提供數(shù)據(jù)訪問保護(hù)旬牲,有可能出現(xiàn)多個(gè)線程先后更改數(shù)據(jù)造成所得到的數(shù)據(jù)是臟數(shù)據(jù)仿粹。

學(xué)習(xí)實(shí)踐

今天在學(xué)習(xí)設(shè)計(jì)模式的時(shí)候,對線程安全的概念有些模糊了原茅,又看到說“線程安全問題都是由靜態(tài)變量引起的”吭历,于是寫了下線程不安全的懶漢式單例模式看線程不安全到底是怎么樣的。話不多說擂橘,代碼如下:

public static void main(String[] args) {
    new Thread() {
        @Override
        public void run() {
            System.out.println("sub thread1 get Singleton instance: " + Singleton.getInstance().toString());
        }
    }.start();

    new Thread() {
        @Override
        public void run() {
            System.out.println("sub thread2 get Singleton instance: " + Singleton.getInstance().toString());
        }
    }.start();

    System.out.println("main thread get Singleton instance: " + Singleton.getInstance().toString());
}

static class Singleton {
    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

多次執(zhí)行發(fā)現(xiàn)出現(xiàn)了下面的結(jié)果:

sub thread1 get Singleton instance: test.TestSync$Singleton@1670207
sub thread2 get Singleton instance: test.TestSync$Singleton@1e21217
main thread get Singleton instance: test.TestSync$Singleton@1db9742

哇晌区,三個(gè)線程都得到了不同的Singleton對象,當(dāng)然這種情況少一些通贞。也就是說當(dāng)這幾個(gè)線程同時(shí)執(zhí)行時(shí)朗若,由于CPU的調(diào)度不同(執(zhí)行一個(gè)線程,執(zhí)行一部分代碼昌罩,讓它等待或叫阻塞哭懈,接著運(yùn)行另一個(gè)線程,執(zhí)行一部分又阻塞它......)茎用,3個(gè)線程可能都判斷singleton為null遣总,然后都分別執(zhí)行singleton = new Singleton();所以產(chǎn)生了3個(gè)不同的對象睬罗。
這個(gè)好理解,但是我有點(diǎn)好奇旭斥,難道一般的變量在被多個(gè)線程訪問時(shí)一定是線程安全的容达?于是我又進(jìn)行了下面的嘗試:

public static void main(String[] args) {
    ObjTest objTest = new ObjTest();
    if(objTest != null){ // 為了防止線程里的代碼先于上一行代碼執(zhí)行
        new Thread() {
            @Override
            public void run() {
                System.out.println("sub thread1 get instance: " + objTest.getInstance().toString());
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                System.out.println("sub thread2 get instance: " + objTest.getInstance().toString());
            }
        }.start();
        
        new Thread() {
            @Override
            public void run() {
                System.out.println("sub thread3 get instance: " + objTest.getInstance().toString());
            }
        }.start();
        
        new Thread() {
            @Override
            public void run() {
                System.out.println("sub thread4 get instance: " + objTest.getInstance().toString());
            }
        }.start();

        System.out.println("main thread get instance: " + objTest.getInstance().toString());
    }
}

static class ObjTest {
    private Object object;

    public ObjTest() {
    }

    public Object getInstance() {
        if (object == null) {
            object = new Object();
        }
        return object;
    }
}

這次增加了2個(gè)子線程來測試,經(jīng)過多次執(zhí)行發(fā)現(xiàn)同一個(gè)對象的普通成員變量也是會(huì)有線程不安全的情況垂券。比如結(jié)果如下:

sub thread1 get instance: java.lang.Object@e36b9b
sub thread2 get instance: java.lang.Object@eb3e0a
main thread get instance: java.lang.Object@eb3e0a
sub thread3 get instance: java.lang.Object@eb3e0a
sub thread4 get instance: java.lang.Object@eb3e0a

也就是說花盐,當(dāng)多個(gè)線程同時(shí)訪問成員變量時(shí),不管它是否為靜態(tài)變量菇爪,都有可能出現(xiàn)線程不安全的情況算芯。若每個(gè)線程中對靜態(tài)變量只有讀操作,而無寫操作娄帖,一般來說也祠,這個(gè)靜態(tài)變量是線程安全的昙楚;若有多個(gè)線程同時(shí)執(zhí)行寫操作近速,一般都需要考慮線程同步,否則的話就可能影響線程安全堪旧。

如何解決線程不安全呢
  1. 同步方法
    給多線程訪問的成員方法加上synchronized修飾符
public void synchronized doWork(){
     // TODO
}
  • 使用synchronized修飾的方法削葱,就叫做同步方法,保證線程執(zhí)行該方法的時(shí)候淳梦,其他線程只能在方法外等著析砸。
  1. 同步代碼塊
synchronized(同步鎖對象)
{
     // 需要同步操作的代碼
}
  • 實(shí)際上,對象的同步鎖只是一個(gè)概念爆袍,可以想象為在對象上標(biāo)記了一個(gè)鎖首繁,誰拿到鎖,誰就可以進(jìn)入代碼塊陨囊,其他線程只能在代碼塊外面等著弦疮,而且注意,在任何時(shí)候蜘醋,Java虛擬機(jī)最多允許一個(gè)線程擁有該同步鎖胁塞。
  • Java程序運(yùn)行可以使用任何對象作為同步監(jiān)聽對象,但是一般的压语,我們把當(dāng)前并發(fā)訪問的共同資源作為同步監(jiān)聽對象啸罢。

實(shí)際上,同步方法和同步代碼塊差不了多少胎食,在本質(zhì)上是一樣的扰才,兩者都用了一個(gè)關(guān)鍵字synchronized,synchronized保證了多線程并發(fā)訪問時(shí)的同步操作厕怜,避免線程的安全性問題训桶,但是有一個(gè)弊端累驮,就是使用synchronized的方法/代碼塊的性能比不用要低一些,因此如果要用synchronized舵揭,建議盡量減小synchronized的作用域谤专。

第一次寫博客呢,不知道寫得怎么樣午绳。有什么意見和建議歡迎評論共同學(xué)習(xí)進(jìn)步~(≧▽≦)/啦啦啦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末置侍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拦焚,更是在濱河造成了極大的恐慌蜡坊,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赎败,死亡現(xiàn)場離奇詭異秕衙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)僵刮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門据忘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搞糕,你說我怎么就攤上這事勇吊。” “怎么了窍仰?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵汉规,是天一觀的道長。 經(jīng)常有香客問我驹吮,道長针史,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任碟狞,我火速辦了婚禮啄枕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘篷就。我一直安慰自己射亏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布竭业。 她就那樣靜靜地躺著智润,像睡著了一般。 火紅的嫁衣襯著肌膚如雪未辆。 梳的紋絲不亂的頭發(fā)上窟绷,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機(jī)與錄音咐柜,去河邊找鬼兼蜈。 笑死攘残,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的为狸。 我是一名探鬼主播歼郭,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼辐棒!你這毒婦竟也來了病曾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤漾根,失蹤者是張志新(化名)和其女友劉穎泰涂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辐怕,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逼蒙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寄疏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片是牢。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赁还,靈堂內(nèi)的尸體忽然破棺而出妖泄,到底是詐尸還是另有隱情驹沿,我是刑警寧澤艘策,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站渊季,受9級特大地震影響朋蔫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜却汉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一驯妄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧合砂,春花似錦青扔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至缘屹,卻和暖如春凛剥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轻姿。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工犁珠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逻炊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓犁享,卻偏偏與公主長得像余素,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子炊昆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

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

  • 從三月份找實(shí)習(xí)到現(xiàn)在溺森,面了一些公司,掛了不少窑眯,但最終還是拿到小米屏积、百度、阿里磅甩、京東炊林、新浪、CVTE卷要、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,275評論 11 349
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法渣聚,類相關(guān)的語法,內(nèi)部類的語法僧叉,繼承相關(guān)的語法奕枝,異常的語法,線程的語...
    子非魚_t_閱讀 31,663評論 18 399
  • 一瓶堕、進(jìn)程和線程 進(jìn)程 進(jìn)程就是一個(gè)執(zhí)行中的程序?qū)嵗溃總€(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間,一個(gè)進(jìn)程中可以有多個(gè)線程郎笆。...
    阿敏其人閱讀 2,612評論 0 13
  • Java8張圖 11谭梗、字符串不變性 12、equals()方法宛蚓、hashCode()方法的區(qū)別 13激捏、...
    Miley_MOJIE閱讀 3,709評論 0 11
  • 看了不少官場小說,讀的時(shí)候很暢快凄吏,想一直看下去远舅,看完卻很空虛,不知道有什么實(shí)際意義痕钢,囫圇吞棗图柏,看過忘過,似乎什么也...
    kwork1988閱讀 330評論 0 1