理解Java中的線程安全及處理方法

1. 線程安全性

1.1. 繼承方式VS實(shí)現(xiàn)方式(掌握)

當(dāng)多線程并發(fā)訪問同一個(gè)資源時(shí)纯衍,會(huì)導(dǎo)致線程出現(xiàn)安全性的原因蔗衡,看案例。

案例:現(xiàn)有50個(gè)蘋果蝙叛,現(xiàn)在有請(qǐng)三個(gè)童鞋(小A俺祠、小B、小C)上臺(tái)表演吃蘋果借帘。

因?yàn)锳蜘渣、B、C三個(gè)人可以同時(shí)吃蘋果肺然,此時(shí)得使用多線程技術(shù)來實(shí)現(xiàn)這個(gè)案例蔫缸。

分析: 可以定義三個(gè)線程對(duì)象,并啟動(dòng)線程.

第一步:每一個(gè)同學(xué)吃蘋果的時(shí)候:先展示自己拿到手上蘋果的編號(hào)际起,如1拾碌,2,3街望,36...

第二步:再吃掉蘋果(意味著蘋果的總數(shù)少一個(gè))

方式1:可以使用繼承Thread方式來實(shí)現(xiàn).

方式2:可以使用實(shí)現(xiàn)Runnable方式來實(shí)現(xiàn).

使用繼承方式

class Person extends Thread {

    private int num = 50;//蘋果總數(shù)

    public Person(String name) {

        super(name);

    }

    public void run() {

        for (int i = 0; i < 50; i++) {

            if (num > 0) {

                System.out.println(super.getName() + "吃了編號(hào)為:" + num-- + "的蘋果");

            }

        }

    }

}

public class ThreadDemo1 {

    public static void main(String[] args) {

        //創(chuàng)建三個(gè)線程倦沧,吃蘋果

        new Person("小A").start();

        new Person("小B").start();

        new Person("小C").start();

    }

}

使用繼承方式完成該案例的時(shí)候,會(huì)發(fā)現(xiàn)A它匕、B展融、C都各自吃了50個(gè)蘋果,為何?

使用實(shí)現(xiàn)方式

class Apple implements Runnable {

    private int num = 50;//蘋果總數(shù)

    public void run() {

        for (int i = 0; i < 50; i++) {

            if (num > 0) {

                System.out.println(Thread.currentThread().getName()

+ "吃了編號(hào)為:" + num-- + "的蘋果");

            }

        }

    }

}

public class ThreadDemo2 {

    public static void main(String[] args) {

        Apple a = new Apple();

        //創(chuàng)建三個(gè)線程告希,吃蘋果

        new Thread(a, "小A").start();

        new Thread(a, "小B").start();

        new Thread(a, "小C").start();

    }

}

在使用實(shí)現(xiàn)方式的時(shí)候扑浸,我們發(fā)現(xiàn)A、B燕偶、C一共吃了50個(gè)蘋果喝噪,為何?

通過吃蘋果比賽指么,分析繼承方式和實(shí)現(xiàn)方式的區(qū)別:

繼承方式:

  • Java中類是單繼承的酝惧,如果繼承了Thread了,該類就不能再有其他的直接父類了伯诬。

  • 從操作上分析晚唇,繼承方式更簡(jiǎn)單,獲取線程名字也簡(jiǎn)單盗似。

  • 從多線程共享同一個(gè)資源上分析哩陕,繼承方式不能多個(gè)線程共享同一個(gè)資源。

實(shí)現(xiàn)方式:

  • Java中類可以多實(shí)現(xiàn)接口赫舒,此時(shí)該類還可以繼承其他類悍及,并且還可以實(shí)現(xiàn)其他接口(設(shè)計(jì)上,更優(yōu)雅)。

  • 從操作上分析接癌,獲取線程名字也比較復(fù)雜心赶,得使用Thread.currentThread()來獲取當(dāng)前線程的引用。

  • 從多線程共享同一個(gè)資源上分析缺猛,實(shí)現(xiàn)方式可以多線程共享同一個(gè)資源园担。

1.2. 線程同步(掌握)

當(dāng)多線程并發(fā)訪問同一個(gè)資源對(duì)象的時(shí)候,可能出現(xiàn)線程不安全的問題。

但是枯夜,分析打印的結(jié)果弯汰,有時(shí)候發(fā)現(xiàn)沒有問題:

意識(shí):看不到問題,不代表沒有問題湖雹,可能是我們經(jīng)驗(yàn)不夠咏闪,或者說問題出現(xiàn)的不夠明顯。

那么可以使用線程休眠來模擬網(wǎng)絡(luò)延遲摔吏,讓問題來得更明顯一些:

Thread.sleep(10);//當(dāng)前線程睡10毫秒,當(dāng)前線程休息著,讓其他線程去搶資源.           

在程序中并不是使用Thread.sleep(10)之后程序才出現(xiàn)問題鸽嫂,而是使用之后,問題更明顯征讲,休眠的時(shí)間越久問題越明顯据某,一般用10或100即可,具體根據(jù)情況而定诗箍。

class Apple implements Runnable {

    private int num = 50;//蘋果總數(shù)

    public void run() {

        try {

            for (int i = 0; i < 50; i++) {

                if (num > 0) {

                    Thread.sleep(10);

                    System.out.println(Thread.currentThread().getName()

 + "吃了編號(hào)為:" + num-- + "的蘋果");

                }

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

分析運(yùn)行結(jié)果癣籽,為什么有錯(cuò)誤的結(jié)果。

在這里,打印蘋果的編號(hào)和蘋果數(shù)量的減少筷狼,應(yīng)該是一個(gè)原子操作瓶籽,也就說是一個(gè)不能分割的操作,兩個(gè)步驟之間不能被其他線程插一腳埂材。

第一步:System.out.println(Thread.currentThread().getName()

  • "吃了編號(hào)為:" + num + "的蘋果");

第二步:num --;

解決方案:保證打印蘋果編號(hào)和蘋果總數(shù)減1操作塑顺,必須同步完成。

解決思路:A線程獲得同步鎖進(jìn)入操作的時(shí)候俏险,B和C線程只能在外等著严拒,A操作結(jié)束,釋放同步鎖竖独。A和B和C才有機(jī)會(huì)去搶同步鎖(誰獲得同步鎖裤唠,誰才能執(zhí)行代碼)。

通俗例子:A预鬓、B巧骚、C三個(gè)人去搶廁所的雅間赊颠,為了保證安全規(guī)定誰搶到了必須上鎖格二,把其他人排除外雅間外面。若A搶到了竣蹦,進(jìn)入后應(yīng)該立馬上鎖顶猜,B和C只能在外等著,當(dāng)A釋放鎖出來的時(shí)候痘括,A长窄、B、C又開始嘗試搶資源纲菌。

  • 方式1:同步代碼塊

  • 方式2:同步方法

1.2.1. 同步代碼塊(掌握)

同步代碼塊語法:

synchronized(同步鎖){

     需要同步操作的代碼

}

同步鎖挠日,又稱之為同步監(jiān)聽對(duì)象/同步鎖/同步監(jiān)聽器/互斥鎖:

為了保證每個(gè)線程都能正常執(zhí)行原子操作,Java引入了線程同步機(jī)制翰舌。

對(duì)象的同步鎖只是一個(gè)概念嚣潜,可以想象為在對(duì)象上標(biāo)記了一個(gè)鎖。

Java程序允許使用任何對(duì)象作為同步監(jiān)聽對(duì)象椅贱,一般的懂算,我們把當(dāng)前并發(fā)訪問的共同資源作為同步監(jiān)聽對(duì)象,比如此時(shí)三個(gè)線程的共同資源Apple對(duì)象庇麦。

注意:在任何時(shí)候计技,最多允許一個(gè)線程擁有同步鎖,誰拿到鎖就執(zhí)行山橄,其他的線程只能在代碼塊外等著垮媒。

class Apple implements Runnable {

    private int num = 50;//蘋果總數(shù)

    public void run() {

        try {

            for (int i = 0; i < 50; i++) {

                synchronized (this) {

                    if (num > 0) {

                        Thread.sleep(10);

                        System.out.println(Thread.currentThread().getName()

+ "吃了編號(hào)為:" + num-- + "的蘋果");

                    }

                }

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

此時(shí)的同步鎖this表示Apple對(duì)象,而程序中Apple對(duì)象只有一份,故可以作為同步鎖涣澡。

1.2.2. 同步方法(掌握)

使用synchronized修飾的方法贱呐,就叫做同步方法。保證A線程執(zhí)行該方法的時(shí)候入桂,其他線程只能在方法外等著奄薇。

synchronized  public  void  doWork(){

     ///TODO

}

此時(shí)同步鎖是誰——其實(shí)就是,調(diào)用當(dāng)前同步方法的對(duì)象:

  • 對(duì)于非static方法抗愁,同步鎖就是this馁蒂。

  • 對(duì)于static方法,同步鎖就是當(dāng)前方法所在類的字節(jié)碼對(duì)象蜘腌。

class Apple implements Runnable {

    private int num = 50;//蘋果總數(shù)

    public void run() {

        for (int i = 0; i < 50; i++) {

            this.eat();

        }

    }

    synchronized private void eat() {

        try {

            if (num > 0) {

                Thread.sleep(10);

                System.out.println(Thread.currentThread().getName()

+ "吃了編號(hào)為:" + num-- + "的蘋果");

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

1.2.3. synchronized的優(yōu)劣(掌握)

好處:保證了多線程并發(fā)訪問時(shí)的同步操作沫屡,避免線程的安全性問題。

缺點(diǎn):使用synchronized的方法/代碼塊的性能要低一些撮珠。

建議:盡量減小synchronized的作用域沮脖。

面試題:

1、StringBuilder和StringBuffer的區(qū)別

2芯急、說說ArrayList和Vector的區(qū)別

3勺届、HashMap和Hashtable的區(qū)別

通過源代碼會(huì)發(fā)現(xiàn),主要就是方法有沒有使用synchronized的區(qū)別娶耍,比如StringBuilder和StringBuffer免姿。

image.png

因此得出結(jié)論:使用synchronized修飾的方法性能較高,但是安全性較低榕酒,反之則反胚膊。

若要獲得最好的學(xué)習(xí)效果,需要配合對(duì)應(yīng)教學(xué)視頻一起學(xué)習(xí)想鹰。需要完整教學(xué)視頻紊婉,請(qǐng)參看https://ke.qq.com/course/272077

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辑舷,一起剝皮案震驚了整個(gè)濱河市喻犁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惩妇,老刑警劉巖株汉,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異歌殃,居然都是意外死亡乔妈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門氓皱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來路召,“玉大人勃刨,你說我怎么就攤上這事」傻” “怎么了身隐?”我有些...
    開封第一講書人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)唯灵。 經(jīng)常有香客問我贾铝,道長(zhǎng),這世上最難降的妖魔是什么埠帕? 我笑而不...
    開封第一講書人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任垢揩,我火速辦了婚禮,結(jié)果婚禮上敛瓷,老公的妹妹穿的比我還像新娘叁巨。我一直安慰自己,他們只是感情好呐籽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開白布锋勺。 她就那樣靜靜地躺著,像睡著了一般狡蝶。 火紅的嫁衣襯著肌膚如雪庶橱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,043評(píng)論 1 291
  • 那天牢酵,我揣著相機(jī)與錄音悬包,去河邊找鬼衙猪。 笑死馍乙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的垫释。 我是一名探鬼主播丝格,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼棵譬!你這毒婦竟也來了显蝌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤订咸,失蹤者是張志新(化名)和其女友劉穎曼尊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脏嚷,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骆撇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了父叙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片神郊。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肴裙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涌乳,到底是詐尸還是另有隱情蜻懦,我是刑警寧澤,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布夕晓,位于F島的核電站宛乃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蒸辆。R本人自食惡果不足惜烤惊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吁朦。 院中可真熱鬧柒室,春花似錦、人聲如沸逗宜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纺讲。三九已至擂仍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間熬甚,已是汗流浹背逢渔。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乡括,地道東北人肃廓。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像诲泌,于是被迫代替她去往敵國(guó)和親盲赊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351

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