Java多線程編程核心技術(shù)--第二章

乖乖兒這章東西真多,對(duì)于我這個(gè)不愛(ài)看書(shū)的人岗屏,真是受不了啊辆琅,這章代碼多,理論也不少这刷,需要靜心整理婉烟,不過(guò)這章也是多線程技術(shù)的重中之重,所以必須要堅(jiān)持下去暇屋。come on似袁! ----- 這里筆記基本都是一個(gè)理論一個(gè)代碼示例,用代碼證明理論這種抽象的東西

synchronized同步方法

線程安全:獲得的實(shí)例變量的值是經(jīng)過(guò)同步處理的,不會(huì)出現(xiàn)臟讀的現(xiàn)象昙衅。
非線程安全:多個(gè)線程對(duì)同一個(gè)對(duì)象中的實(shí)例變量進(jìn)行并發(fā)訪問(wèn)時(shí)發(fā)生扬霜,產(chǎn)生的后果就是“臟讀”。

同步方法的一些特性

  • 方法內(nèi)的變量為線程安全(每一個(gè)線程操作自己的局部變量)
class HashSelfPrivateNum{
    public void setNum(String name){
        int num = 0; // This is important
        if("a".equals(name)){
            num = 100;
            System.out.println("a set over");
        }else{
            num = 200;
            System.out.println(name +" set over");
        }

        try{
            Thread.sleep(1000);
            System.out.println(name + " : num = " + num);
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}

class ThreadOne extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadOne(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadOne");
        }

        public  void run(){
            numRef.setNum("a");
    }
}

class ThreadTwo extends Thread{
    private HashSelfPrivateNum numRef;

    public ThreadTwo(HashSelfPrivateNum numRef){
        this.numRef = numRef;
        this.setName("ThreadTwo");
    }

    public  void run(){
        numRef.setNum("over");
  }
}

public class Demo{
    public static void main(String[] args) throws Exception{
        HashSelfPrivateNum numRef = new HashSelfPrivateNum();
        ThreadOne one = new ThreadOne(numRef);
        ThreadTwo two = new ThreadTwo(numRef);
        one.start();
        two.start();
    }
}

運(yùn)行結(jié)果:

over set over
a set over
over : num = 200
a : num = 100
  • 實(shí)例變量是非線程安全(多個(gè)線程操作同一個(gè)實(shí)例變量)

class HashSelfPrivateNum{
    private int num; // This is important

    public void setNum(String name){
        if("a".equals(name)){
            num = 100;
            System.out.println("a set over");
        }else{
            num = 200;
            System.out.println(name +"set over");
        }

        try{
            Thread.sleep(1000);
            System.out.println(name + " : num = " + num);
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}

class ThreadOne extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadOne(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadOne");
        }

        public  void run(){
            numRef.setNum("a");
    }
}

class ThreadTwo extends Thread{
    private HashSelfPrivateNum numRef;

    public ThreadTwo(HashSelfPrivateNum numRef){
        this.numRef = numRef;
        this.setName("ThreadTwo");
    }

    public  void run(){
        numRef.setNum("over");
  }
}

public class Demo{
    public static void main(String[] args) throws Exception{
        HashSelfPrivateNum numRef = new HashSelfPrivateNum();
        ThreadOne one = new ThreadOne(numRef);
        ThreadTwo two = new ThreadTwo(numRef);
        one.start();
        two.start();
    }
}

運(yùn)行結(jié)果:

a set over
overset over
over : num = 200
a : num = 200

在這里我們可以清晰的看見(jiàn)數(shù)據(jù)發(fā)生了錯(cuò)誤而涉,明明a應(yīng)該是100結(jié)果卻是200.這就是多個(gè)線程訪問(wèn)同一個(gè)實(shí)例變量帶來(lái)的隱患著瓶。
避免這種隱患的方式就是在setNum方法前加一個(gè)synchronized關(guān)鍵字,讓setNum方法成為同步方法啼县。

  • 多個(gè)對(duì)象對(duì)應(yīng)多個(gè)鎖(每個(gè)對(duì)象都有自己的鎖材原,并不是公用同一個(gè))
    先將上面的代碼加上synchronized看看運(yùn)行結(jié)果

class HashSelfPrivateNum{
    private int num; // This is important

    public synchronized void setNum(String name){
        if("a".equals(name)){
            num = 100;
            System.out.println("a set over");
        }else{
            num = 200;
            System.out.println(name +"set over");
        }

        try{
            Thread.sleep(1000);
            System.out.println(name + " : num = " + num);
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}

class ThreadOne extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadOne(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadOne");
        }

        public  void run(){
            numRef.setNum("a");
    }
}

class ThreadTwo extends Thread{
    private HashSelfPrivateNum numRef;

    public ThreadTwo(HashSelfPrivateNum numRef){
        this.numRef = numRef;
        this.setName("ThreadTwo");
    }

    public  void run(){
        numRef.setNum("over");
  }
}

public class Demo{
    public static void main(String[] args) throws Exception{
        HashSelfPrivateNum numRef = new HashSelfPrivateNum();
        //HashSelfPrivateNum numRefTwo = new HashSelfPrivateNum();
        ThreadOne one = new ThreadOne(numRef);
        ThreadTwo two = new ThreadTwo(numRef);
        one.start();
        two.start();
    }
}

運(yùn)行結(jié)果:

a set over
a : num = 100
overset over
over : num = 200

從結(jié)果可以看出線程O(píng)ne先執(zhí)行完setNum方法,線程Two才執(zhí)行setNum季眷。

接下來(lái)證明本小節(jié)這個(gè)理論
將第53行注釋拿掉余蟹,再將55行的numRef改為numRefTwo。

運(yùn)行結(jié)果:

a set over
overset over
a : num = 100
over : num = 200

我們發(fā)現(xiàn)線程O(píng)ne執(zhí)行setNum方法還沒(méi)執(zhí)行完子刮,線程Two就開(kāi)始執(zhí)行setNum方法威酒,這里說(shuō)明線程O(píng)ne執(zhí)行setNum方法是拿的numRef對(duì)象的鎖,線程Two執(zhí)行setNum方法拿的是numRefTwo對(duì)象的鎖话告,他們互不干擾兼搏。

  • synchronized 方法鎖的是對(duì)象
  • synchronized 鎖的重入(當(dāng)一個(gè)線程的一個(gè)對(duì)象鎖后,再次請(qǐng)求此對(duì)象鎖是可以再次得到該對(duì)象的鎖的)
  • 出現(xiàn)異常鎖自動(dòng)釋放

class HashSelfPrivateNum{

    public synchronized void setNum(String name){
        System.out.println(Thread.currentThread().getName() + " set over");
        if("a".equals(name)){
            Integer.parseInt("adb");
        }
        try{
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " end");
        }catch(Exception e){
            e.printStackTrace();
        }

    }

}

class ThreadOne extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadOne(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadOne");
        }

        public  void run(){
            numRef.setNum("a");
    }
}

class ThreadTwo extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadTwo(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadTwo");
        }

        public  void run(){
            numRef.setNum("over");
        }
}

public class Demo{
    public static void main(String[] args) throws Exception{
        HashSelfPrivateNum numRef = new HashSelfPrivateNum();
        ThreadOne one = new ThreadOne(numRef);
        ThreadTwo two = new ThreadTwo(numRef);
        one.start();
        two.start();
    }
}

運(yùn)行結(jié)果:

ThreadOne set over
Exception in thread "ThreadOne" ThreadTwo set over
java.lang.NumberFormatException: For input string: "adb"
        at java.lang.NumberFormatException.forInputString(Unknown Source)
        at java.lang.Integer.parseInt(Unknown Source)
        at java.lang.Integer.parseInt(Unknown Source)
        at HashSelfPrivateNum.setNum(Demo.java:7)
        at ThreadOne.run(Demo.java:29)
ThreadTwo end

我們可以清楚看見(jiàn)當(dāng)ThreadOne拋出異常ThreadTwo立刻拿到numRef對(duì)象的鎖沙郭,繼續(xù)執(zhí)行了下去佛呻。

  • 同步不具有繼承性

當(dāng)我們?cè)诟割悓?xiě)了一個(gè)同步方法,如果在子類重寫(xiě)此方法還需要我們重新寫(xiě)上synchronized關(guān)鍵字病线。

synchronized 同步語(yǔ)句塊

效率吓著! 效率! 效率送挑!

synchronized 同步語(yǔ)句塊的一些特性

  • 彌補(bǔ)同步方法的低效性

同步方法的局限性導(dǎo)致的原因是synchronized的作用域在整個(gè)方法绑莺,這將導(dǎo)致方法中無(wú)需同步的代碼也必須要同步。
例如在同步方法里有一個(gè)非常耗時(shí)的請(qǐng)求操作惕耕,然而我們只需要在請(qǐng)求得到數(shù)據(jù)后在同步設(shè)置方法纺裁。

優(yōu)化代碼:


class HashSelfPrivateNum{
    private int num; // This is important

    public  void setNum(String name){
        System.out.println(Thread.currentThread().getName() + " set over");

        try{
            Thread.sleep(5000); // 模擬非常大的數(shù)據(jù)請(qǐng)求
        }catch(Exception e){
            e.printStackTrace();
        }

        synchronized(this){
            this.num = 100;
            System.out.println(Thread.currentThread().getName() + " end");
            }
        }
}

class ThreadOne extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadOne(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadOne");
        }

        public  void run(){
            numRef.setNum("a");
    }
}

class ThreadTwo extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadTwo(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadTwo");
        }

        public  void run(){
            numRef.setNum("over");
        }
}

public class Demo{
    public static void main(String[] args) throws Exception{
        HashSelfPrivateNum numRef = new HashSelfPrivateNum();
        ThreadOne one = new ThreadOne(numRef);
        ThreadTwo two = new ThreadTwo(numRef);
        one.start();
        two.start();
    }
}

輸出結(jié)果:


ThreadOne set over
ThreadTwo set over
ThreadOne end
ThreadTwo end

  • synchronized 代碼塊間的同步性

class HashSelfPrivateNum{

    public synchronized void setNum(String name){
        System.out.println(Thread.currentThread().getName() + " set over");

        try{
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " end");
        }catch(Exception e){
            e.printStackTrace();
        }

    }

    public void print(){
        synchronized(this){
            try{
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + " print");
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

class ThreadOne extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadOne(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadOne");
        }

        public  void run(){
            numRef.setNum("a");
    }
}

class ThreadTwo extends Thread{
        private HashSelfPrivateNum numRef;

        public ThreadTwo(HashSelfPrivateNum numRef){
            this.numRef = numRef;
            this.setName("ThreadTwo");
        }

        public  void run(){
            numRef.print();
        }
}

public class Demo{
    public static void main(String[] args) throws Exception{
        HashSelfPrivateNum numRef = new HashSelfPrivateNum();
        ThreadOne one = new ThreadOne(numRef);
        ThreadTwo two = new ThreadTwo(numRef);
        one.start();
        two.start();
    }
}

輸出結(jié)果:


ThreadOne set over
ThreadOne end
ThreadTwo print

  • 將任意對(duì)象作為對(duì)象監(jiān)聽(tīng)器

對(duì)于一個(gè)實(shí)例我們可以讓讀和寫(xiě)方法間進(jìn)行同步,但是如果是其他無(wú)關(guān)方法呢司澎?如果我們也讓他們保持同步那該多么影響效率啊~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末欺缘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子挤安,更是在濱河造成了極大的恐慌谚殊,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛤铜,死亡現(xiàn)場(chǎng)離奇詭異嫩絮,居然都是意外死亡丛肢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)剿干,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蜂怎,“玉大人,你說(shuō)我怎么就攤上這事置尔∨煞螅” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵撰洗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我腐芍,道長(zhǎng)差导,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任猪勇,我火速辦了婚禮设褐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泣刹。我一直安慰自己助析,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布椅您。 她就那樣靜靜地躺著外冀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掀泳。 梳的紋絲不亂的頭發(fā)上雪隧,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音员舵,去河邊找鬼脑沿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛马僻,可吹牛的內(nèi)容都是我干的庄拇。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼韭邓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼措近!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起仍秤,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤熄诡,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后诗力,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體凰浮,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡我抠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袜茧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菜拓。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笛厦,靈堂內(nèi)的尸體忽然破棺而出纳鼎,到底是詐尸還是另有隱情,我是刑警寧澤裳凸,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布贱鄙,位于F島的核電站,受9級(jí)特大地震影響姨谷,放射性物質(zhì)發(fā)生泄漏逗宁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一梦湘、第九天 我趴在偏房一處隱蔽的房頂上張望瞎颗。 院中可真熱鬧,春花似錦捌议、人聲如沸哼拔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)倦逐。三九已至,卻和暖如春弄捕,著一層夾襖步出監(jiān)牢的瞬間僻孝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工守谓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穿铆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓斋荞,卻偏偏與公主長(zhǎng)得像荞雏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子平酿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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