Java 多線程

1 簡(jiǎn)述

1.1 線程機(jī)制

Java使用的是搶占式的線程機(jī)制魂迄,調(diào)度機(jī)制周期性地切換上下文,切換線程惋耙,從而為每個(gè)線程提供時(shí)間片捣炬⌒懿看似同時(shí)執(zhí)行,其實(shí)是不停地切換湿酸。在這種機(jī)制下婿屹,一個(gè)線程的阻塞不會(huì)導(dǎo)致整個(gè)進(jìn)程阻塞。

優(yōu)先級(jí)較低的線程僅僅是執(zhí)行的頻率較低推溃,不會(huì)得不到執(zhí)行昂利。

要實(shí)現(xiàn)線程行為,你必須顯示地將一個(gè)任務(wù)(Runnable)附著到線程(Thread)上铁坎。Thread類只是驅(qū)動(dòng)賦予它的任務(wù)Runnable蜂奸。

1.2 阻塞(se第四聲)

程序中一個(gè)任務(wù)因?yàn)樵摮绦蚩刂品秶獾囊蛩兀l件通常是IO),而不能繼續(xù)執(zhí)行厢呵,這個(gè)任務(wù)線程被阻塞了窝撵。

2 Executor 線程池

用new Thread()的方式不利于性能優(yōu)化,所以采用線程池替代襟铭,達(dá)到線程的復(fù)用碌奉。首選CachedThreadPool回收舊線程停止創(chuàng)建新線程。

ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Runnable() {
        @Override
        public void run() {

        }
});

Runnable設(shè)返回值寒砖,需要實(shí)現(xiàn)Callable接口call()方法赐劣,并且必須使用ExecutorService.submit()方法調(diào)用。
使用 Future.get() 獲取返回值哩都。
例如:

public class CallableAndFuture {
    static class MyThread implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "Hello world";
        }
    }

    static class MyThread2 implements Runnable {
        @Override
        public void run() {

        }
    }

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Future<String> future = threadPool.submit(new MyThread());

        try {
            System.out.println(future.get());
        } catch (Exception e) {

        } finally {
            threadPool.shutdown();
        }
    }
}

3 線程安全

3.1 實(shí)質(zhì)

線程安全其實(shí)就是解決共享資源競(jìng)爭(zhēng)的問(wèn)題魁兼。
解決方法是:當(dāng)一個(gè)資源被一個(gè)任務(wù)使用時(shí),加鎖漠嵌。

解鎖的時(shí)候咐汞,下一個(gè)要使用資源的任務(wù)并沒(méi)有按照排隊(duì)的方式按次序來(lái)獲取,而是通過(guò)競(jìng)爭(zhēng)獲取資源儒鹿』海可以通過(guò)yield()和setPriority()來(lái)給線程調(diào)度器提供建議,這效果取決于具體平臺(tái)和JVM實(shí)現(xiàn)约炎。

3.2 synchronized加鎖

對(duì)于某個(gè)特定對(duì)象植阴,其所有的synchronized方法共享同一個(gè)鎖。
一個(gè)任務(wù)可以多次獲得對(duì)象的鎖圾浅。

判斷是否應(yīng)該加鎖:如果你正在寫一個(gè)變量掠手,這個(gè)變量接下來(lái)將被另一個(gè)線程讀取;你正在讀一個(gè)上一次已經(jīng)被另一個(gè)線程寫過(guò)的變量狸捕。以上兩種情況都必須使用同步喷鸽。并且,讀寫線程都必須用相同的監(jiān)視器鎖同步府寒。

3.3 使用顯式的Lock對(duì)象加鎖

除了synchronized方式加鎖之外魁衙,還可以用Lock對(duì)象:

private Lock lock = new ReentrantLock();//重入鎖
......
lock.lock();
try{
......
return ...;//在try中return確保unlock()不會(huì)過(guò)早發(fā)生
}finally{
lock.unlock();
}

和synchronized的區(qū)別:tryLock()

  1. 可以嘗試著獲取鎖报腔,最終獲取失敗。
  2. 可以嘗試著獲取鎖一段時(shí)間剖淀,然后放棄獲取纯蛾。

如果其他的線程已經(jīng)獲取了這個(gè)鎖,你可以決定離開(kāi)纵隔,去執(zhí)行其他一些事翻诉,而不是等待直至這個(gè)鎖釋放。提供了比Synchronized更細(xì)粒度的控制力捌刮。(例如可以釋放當(dāng)前鎖之前碰煌,捕獲下一節(jié)點(diǎn)的鎖)

4 volatile

volatile關(guān)鍵字主要提供2種作用:

  1. 可見(jiàn)性:當(dāng)使用volatile關(guān)鍵字去修飾變量的時(shí)候,所有線程都會(huì)直接讀取該變量并且不緩存它绅作。這就確保了線程讀取到的變量是同內(nèi)存中是一致的
  2. 禁止指令重排序:應(yīng)用在標(biāo)志位

5線程本地存儲(chǔ)

防止共享資源產(chǎn)生沖突的第二種方式是根除變量的共享芦圾,使用ThreadLocal。

6 終結(jié)任務(wù)

線程的各個(gè)狀態(tài)


線程的各個(gè)狀態(tài)

停止線程主要使用標(biāo)志位俄认,在run()方法中加以判斷个少,是否要執(zhí)行。

  1. 自己定義一個(gè)volatile boolean的標(biāo)志位
  2. 使用interrupt()方法眯杏,但是這個(gè)方法也是打個(gè)停止的標(biāo)志夜焦,并不是真正的停止線程,還是要在run()方法中調(diào)用Thread.interrupted()方法判斷岂贩。

7 線程之間的協(xié)作

yield()方法:調(diào)用的線程放棄cpu時(shí)間讓給別的線程茫经。
join()方法:調(diào)用的線程先執(zhí)行。

7.1 wait()

wait()是object的方法萎津,它會(huì)釋放鎖卸伞,而sleep()和yield()不會(huì)釋放。
因此在該對(duì)象(未鎖定的)中的其他synchronized方法可以在wait()期間被調(diào)用锉屈。

只能在同步控制方法或者同步控制塊里調(diào)用wait()瞪慧、notify()、notifyAll()部念,因?yàn)橐僮鲗?duì)象的鎖。

wait()必須用一個(gè)檢查感興趣的條件的while循環(huán)包圍氨菇,本質(zhì)是檢查感興趣的條件儡炼,并在條件不滿足的情況下返回到wait()方法中。

Thread1:
synchronized(sharedMonitor){
  <setup condition for Thread2>
  sharedMonitor.notify();
}
//錯(cuò)誤的使用:時(shí)機(jī)太晚查蓉,錯(cuò)失信號(hào)乌询,盲目進(jìn)入wait(),產(chǎn)生死鎖豌研。
Thread2:
while(someConditon){
  synchronized(sharedMonitor){
    sharedMonitor.wait();
  }
}
//正確的使用:防止在someCondition變量上產(chǎn)生競(jìng)爭(zhēng)條件
Thread2:
 synchronized(sharedMonitor){
    while(someConditon){
         sharedMonitor.wait();
    }
 }

7.2 notify()和notifyAll()

notify():在眾多等待同一個(gè)鎖的線程中妹田,只有一個(gè)會(huì)被喚醒去獲取鎖唬党。
notifyAll():在眾多等待同一個(gè)鎖的線程中,全部會(huì)被喚醒去共同競(jìng)爭(zhēng)鎖鬼佣。
兩個(gè)同樣最終只有一個(gè)線程能獲取到鎖驶拱。

可能有多個(gè)線程某單個(gè)對(duì)象上處于wait狀態(tài),因此調(diào)用notifyAll()比notify()更安全晶衷。
只有一個(gè)線程實(shí)際處于wait()狀態(tài)蓝纲,這時(shí)可以用notify()代替notifyAll()優(yōu)化;

如果有多個(gè)線程在等待不同條件晌纫,使用notify()就不能知道是否喚醒了恰當(dāng)?shù)娜蝿?wù)税迷。

8 死鎖

某個(gè)線程在等待另一個(gè)線程,而后者又在等待別的線程锹漱,這樣一直下去箭养,直到這個(gè)鏈上的線程又在等待第一個(gè)線程釋放鎖。這得到了一個(gè)狀態(tài):線程之間相互等待的連續(xù)循環(huán)哥牍,沒(méi)有哪個(gè)線程能繼續(xù)毕泌。這被稱為死鎖

死鎖的4個(gè)條件:

  1. 互斥條件(資源的特點(diǎn)):線程使用的資源中至少有一個(gè)是不能共享的。
  2. 占用請(qǐng)求條件(單個(gè)線程的特點(diǎn)):至少有一個(gè)線程砂心,它持有一個(gè)資源懈词,并且等待獲取另一個(gè)當(dāng)前被別的線程持有的資源。
  3. 不可剝奪條件(單個(gè)線程的特點(diǎn)):已持有的資源不能被別的線程搶占辩诞,而它(持有資源的線程)自己也不會(huì)主動(dòng)去搶別的線程的資源坎弯。
  4. 循環(huán)等待條件(多個(gè)線程形成的特點(diǎn)):必須有循環(huán)等待。

解決:讓一方先放棄資源作出讓步译暂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抠忘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子外永,更是在濱河造成了極大的恐慌崎脉,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伯顶,死亡現(xiàn)場(chǎng)離奇詭異囚灼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)祭衩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門灶体,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人掐暮,你說(shuō)我怎么就攤上這事蝎抽。” “怎么了路克?”我有些...
    開(kāi)封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵樟结,是天一觀的道長(zhǎng)养交。 經(jīng)常有香客問(wèn)我,道長(zhǎng)瓢宦,這世上最難降的妖魔是什么碎连? 我笑而不...
    開(kāi)封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮刁笙,結(jié)果婚禮上破花,老公的妹妹穿的比我還像新娘。我一直安慰自己疲吸,他們只是感情好座每,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著摘悴,像睡著了一般峭梳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蹂喻,一...
    開(kāi)封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天葱椭,我揣著相機(jī)與錄音,去河邊找鬼口四。 笑死孵运,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蔓彩。 我是一名探鬼主播治笨,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赤嚼!你這毒婦竟也來(lái)了旷赖?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤更卒,失蹤者是張志新(化名)和其女友劉穎等孵,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹂空,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俯萌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了上枕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绳瘟。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖姿骏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斤彼,我是刑警寧澤分瘦,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布蘸泻,位于F島的核電站,受9級(jí)特大地震影響嘲玫,放射性物質(zhì)發(fā)生泄漏悦施。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一去团、第九天 我趴在偏房一處隱蔽的房頂上張望抡诞。 院中可真熱鬧,春花似錦土陪、人聲如沸昼汗。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)顷窒。三九已至,卻和暖如春源哩,著一層夾襖步出監(jiān)牢的瞬間鞋吉,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工励烦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谓着,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓坛掠,卻偏偏與公主長(zhǎng)得像赊锚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子却音,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354