簡單聊聊sleep&wait& synchronized

一涡贱、sleep & wait

首先從一個面試問題開篇:我們經(jīng)常用的sleep() & wait() 方法有什么不同洛巢?
基于我自己的知識范圍,我給出以下回答:

  1. sleep方法是Thread類的靜態(tài)方法,而wait方法是Object類下的非靜態(tài)方法
  2. sleep方法 執(zhí)行時不會釋放monitor渣玲;而wait方法在執(zhí)行時會釋放monitor,并將當前線程加入到等待monitor的wait set中弟晚。
  3. 啥時候想讓當前線程休眠了忘衍,直接Thread.sleep(x)就好,不需要依賴monitor卿城;但wait需要
  4. 通常情況下淑履,sleep方法的執(zhí)行線程不需要別人喚醒,但wait()通常需要別人notify()后才能喚醒接著執(zhí)行藻雪,當然wait(x)方法除外秘噪。

對于第二點,通過以下代碼進行驗證勉耀。
關于sleep指煎,我們常規(guī)的sleep寫法:

public static void main(String[] args) {
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

此時通過jstack查看線程狀態(tài)發(fā)現(xiàn):[main線程并沒有在等待monitor]

"main" #1 prio=5 os_prio=31 tid=0x00007fe24d002000 nid=0x1903 waiting on condition [0x0000700006f50000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)

關于wait,我們通過以下代碼來對wait方法進行說明:

public static void main(String[] args) {

        Stream.of("T1", "T2").forEach(name ->
                new Thread(name) {
                    @Override
                    public void run() {
//                        m1();
                    m2();
                    }
                }.start()
        );
    }

    public static void m1() {
        synchronized (LOCK) {
            try {
                System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void m2() {
        synchronized (LOCK) {
            try {
                System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
                LOCK.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

首先注掉m1()便斥,放開m2()至壤。運行程序,得到運行結果:

The Thread T1 enter.
The Thread T2 enter.

這就說明枢纠,在T1獲得LOCK進入synchronized代碼塊之后像街,通過wait將鎖進行了釋放,使得T2嘗試獲取LOCK時能夠成功獲取,并進入synchronzied代碼塊執(zhí)行了println語句镰绎。

那如果注掉m2()放開m1()呢脓斩?結果如下:

The Thread T1 enter.

通過jstack發(fā)現(xiàn),由于T1手里握著鎖畴栖,仍處在synchronized代碼塊中随静,因此T2在嘗試獲取鎖時因為無法獲取而進入阻塞狀態(tài):

"T2" #14 prio=5 os_prio=31 tid=0x00007fd96902a000 nid=0x5b03 waiting for monitor entry [0x000070000be19000]
   java.lang.Thread.State: BLOCKED (on object monitor)

"T1" #13 prio=5 os_prio=31 tid=0x00007fd968895000 nid=0xa903 waiting on condition [0x000070000bd16000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)

關于第三點,做以下測試吗讶,即去掉獲取鎖的代碼為:

    public static void m2() {
//        synchronized (LOCK) {
            try {
                System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
                LOCK.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//        }
    }

執(zhí)行結果:

Exception in thread "T1" Exception in thread "T2" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:502)

所以結論為:需要先獲取鎖才能執(zhí)行wait(包括notify燎猛、notifyAll等)操作

二、synchronized

通過以上代碼我們注意到照皆,synchronized使用的是一個實例化之后的Object類的對象LOCK作為鎖重绷,那就聊聊關于synchronized的幾種寫法,以及這幾種寫法分別把鎖加到了哪里膜毁。
同步代碼塊

final Object LOCK = new Object();

 synchronized (LOCK) {
            try {
                System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

這種寫法就是很明顯的用LOCK對象作為鎖用于線程同步

同步方法
被synchronized所修飾的方法的所在類:

public class SychronizedStatic {
    public synchronized  void m4() {
        System.out.println("m4 " + Thread.currentThread().getName());
        try {
            Thread.sleep(10_000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized  void m5() {
        System.out.println("m5 " + Thread.currentThread().getName());
        try {
            Thread.sleep(10_000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

測試代碼:

public static void main(String[] args) {
        SychronizedStatic sychronizedStatic = new SychronizedStatic();
        new Thread("T4") {
            @Override
            public void run() {
                sychronizedStatic.m4();
            }
        }.start();

        new Thread("T5") {
            @Override
            public void run() {
                sychronizedStatic.m5();
            }
        }.start();
}

這種寫法也比較好理解论寨,作為鎖的對象就是調(diào)用者,即sychronizedStatic爽茴。

靜態(tài)同步方法

被synchronized所修飾的靜態(tài)方法的所在類:

public class SychronizedStatic {
    public synchronized static void m1() {
        System.out.println("m1 " + Thread.currentThread().getName());
        try {
            Thread.sleep(10_000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized static void m2() {
        System.out.println("m2 " + Thread.currentThread().getName());
        try {
            Thread.sleep(10_000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void m3() {
        synchronized (SychronizedStatic.class) {
            System.out.println("m3 " + Thread.currentThread().getName());
            try {
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

測試方法:

    public static void main(String[] args) {
        new Thread("T1") {
            @Override
            public void run() {
                SychronizedStatic.m1();
            }
        }.start();

        new Thread("T2") {
            @Override
            public void run() {
                SychronizedStatic.m2();
            }
        }.start();

}

測試結果如下:

m1 T1

這就說明葬凳,m2()m3()阻塞在了SychronizedStatic.class上,也就是說被synchronized修飾的靜態(tài)方法室奏,在做線程同步時火焰,加鎖是加在靜態(tài)方法所在的靜態(tài)類Class對象上的。

三胧沫、 瞎扯

sleep & wait & synchronize昌简,這都是java開發(fā)最基本的東西,但是如果稍微細問問的話绒怨,還真不一定理解的那么透徹纯赎。因此,學無止境南蹂,要融會貫通~

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末犬金,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子六剥,更是在濱河造成了極大的恐慌栅屏,老刑警劉巖本今,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異太惠,居然都是意外死亡厌秒,警方通過查閱死者的電腦和手機愧哟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門靴庆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人匣摘,你說我怎么就攤上這事」危” “怎么了音榜?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長必指。 經(jīng)常有香客問我,道長恕洲,這世上最難降的妖魔是什么塔橡? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮霜第,結果婚禮上葛家,老公的妹妹穿的比我還像新娘。我一直安慰自己泌类,他們只是感情好癞谒,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刃榨,像睡著了一般弹砚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上枢希,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天桌吃,我揣著相機與錄音,去河邊找鬼苞轿。 笑死茅诱,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的搬卒。 我是一名探鬼主播瑟俭,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼契邀!你這毒婦竟也來了摆寄?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤坯门,失蹤者是張志新(化名)和其女友劉穎椭迎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體田盈,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡畜号,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了允瞧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片简软。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛮拔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痹升,到底是詐尸還是另有隱情建炫,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布疼蛾,位于F島的核電站肛跌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏察郁。R本人自食惡果不足惜衍慎,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望皮钠。 院中可真熱鬧稳捆,春花似錦、人聲如沸麦轰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽款侵。三九已至末荐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間新锈,已是汗流浹背鞠评。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留壕鹉,地道東北人剃幌。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像晾浴,于是被迫代替她去往敵國和親负乡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

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