java并發(fā)基礎(chǔ)知識

1.為什么要使用多線程
  • 充分發(fā)揮多核CPU的性能
  • 方便進(jìn)行業(yè)務(wù)拆分,提升服務(wù)性能
2. java多線程有什么缺點(diǎn)

(1) 頻繁的上下文切換
線程在切換過程中浮庐,CPU需要保存當(dāng)前線程的狀態(tài)雾叭,以便切換回來時(shí)能夠恢復(fù)到當(dāng)前狀態(tài)夷陋,這個(gè)過程會(huì)損耗CPU性能。頻繁的上下文切換無法發(fā)揮多線程的優(yōu)勢汤纸。為了減少上下文切換衩茸,可以采用無鎖并發(fā)編程、CAS算法贮泞、使用最少的線程或是使用協(xié)程

  • 無鎖并發(fā)編程:在有鎖并發(fā)場景中楞慈,線程會(huì)因?yàn)闆]有競爭到鎖而阻塞,讓出CPU啃擦,提前進(jìn)行線程切換
  • CAS操作囊蓝,只有一個(gè)線程能夠執(zhí)行成功,其他線程會(huì)循環(huán)競爭鎖令蛉,直到時(shí)間片執(zhí)行完成
  • 使用少量線程:任務(wù)較少時(shí)聚霜,避免創(chuàng)建過多的線程,以至于多個(gè)線程處于等待狀態(tài)
  • 使用協(xié)程
    協(xié)程概念:基于線程之上珠叔,但是比線程更輕量級的存在蝎宇,由程序員自己寫程序控制
    協(xié)程的目的:當(dāng)線程出現(xiàn)長時(shí)間IO時(shí),由程序控制运杭,掛起當(dāng)前任務(wù)夫啊,并保存當(dāng)前棧信息,去執(zhí)行另一個(gè)任務(wù)辆憔,等待任務(wù)完成或是達(dá)到某個(gè)條件時(shí)撇眯,再還原原先的棧信息,并繼續(xù)執(zhí)行虱咧。
    協(xié)程特點(diǎn):
    (1)線程有OS進(jìn)行調(diào)度熊榛,協(xié)程由用戶自己進(jìn)行調(diào)度。且協(xié)程是在同一線程內(nèi)部操作腕巡,所以可以減少線程上下文的切換
    (2)線程默認(rèn)的stack是1M玄坦,而協(xié)程默認(rèn)是接近1k,所以一個(gè)線程內(nèi)部可以有多個(gè)協(xié)程
    (3)協(xié)程適用于存在阻塞的并發(fā)場景绘沉,而不適用于大量計(jì)算的場景

(2) 線程安全問題
在多線程環(huán)境下煎楣,無論線程以何種順序執(zhí)行,都能保證程序的正確性车伞。線程安全問題择懂,本質(zhì)就是對共享數(shù)據(jù)的訪問問題

3. java線程狀態(tài)

(1) 新建(new):創(chuàng)建后尚未啟動(dòng)的線程處于這個(gè)狀態(tài)
(2) 運(yùn)行(runable): ready + running

  • ready:就緒狀態(tài),等待cpu分配時(shí)間片即可運(yùn)行
  • running:正在運(yùn)行

(3) 無限等待(waiting):處于這個(gè)狀態(tài)的線程不會(huì)被cpu分配時(shí)間片另玖,其等待其他線程顯示喚醒困曙。有如下方法可以進(jìn)入該狀態(tài):

  • 沒有設(shè)置timeout的Object.wait()方法
  • 沒有設(shè)置timeout的Thread.join()方法
  • LockSupport.park()方法

(4) 超時(shí)等待(time_waiting):該狀態(tài)下表伦,線程也不會(huì)被CPU配時(shí)間片,但是與Waiting不同的是慷丽,該狀態(tài)無需等待其他線程顯示喚醒蹦哼,超過超時(shí)時(shí)間之后,系統(tǒng)會(huì)自動(dòng)喚醒線程要糊。有如下方法可以讓線程進(jìn)入超時(shí)等待狀態(tài)

  • Thread.sleep()
  • 設(shè)置了timeout的Object.wait()方法
  • 設(shè)置了timeout的Thread.join()方法
  • LockSupport.parkNanos()方法
  • LockSupport.parkUtils()方法

(5) 阻塞(Blocked): 線程因?yàn)槟撤N原因放棄CPU使用權(quán)纲熏,暫時(shí)停止運(yùn)行。阻塞狀態(tài)分兩種

  • 同步阻塞:線程在進(jìn)入同步代碼塊(synchronize)時(shí)杨耙,未獲得鎖將進(jìn)入這個(gè)狀態(tài)
  • 其他阻塞:正在運(yùn)行的線程發(fā)出IO請求赤套,JVM會(huì)把該線程置為阻塞狀態(tài)

(6) 終止(Terminated):已經(jīng)終止的線程狀態(tài)。線程run()方法或是main()方法執(zhí)行結(jié)束珊膜,或是線程因異常退run方法容握,該線程結(jié)束生命周期

4. 線程生命周期內(nèi)的一些操作

除了新建線程之外,線程生命周期內(nèi)還有一些其他操作车柠,這些操作可以作為線程間的一種通信方式

  1. 中斷 interrupt
    (1) 中斷是什么
    interrupt()就是中斷某個(gè)線程剔氏,主要用于線程間的協(xié)作,如果A線程需要中斷B線程竹祷,就調(diào)用B.interrupt()
    (2) interrupt()一定會(huì)中斷線程嗎
    interrupt()可以看做是線程的一個(gè)標(biāo)志位谈跛,某個(gè)線程被中斷之后,就會(huì)記錄該中斷狀態(tài)塑陵。所以調(diào)用某個(gè)線程的interrupt()之后感憾,并不一定會(huì)真正中斷該線程,僅僅是告知該線程你該中斷了令花,線程是否中斷應(yīng)該由線程自身判斷阻桅,而不是由外部線程決定
    (3) 線程中斷狀態(tài)怎么用
    原則上,在設(shè)計(jì)線程的執(zhí)行流程時(shí)兼都,首先要判斷線程的中斷狀態(tài)來決定執(zhí)行內(nèi)容
    (4) 什么時(shí)候會(huì)拋出InterrupedException
    線程處于wait()嫂沉、sleep()、join()等狀態(tài)扮碧,此時(shí)調(diào)用線程的interrupt()會(huì)拋出InterrupedException趟章,且中斷狀態(tài)會(huì)被清除
    (5) 拋出的InterruptException該怎么處理
    • 繼續(xù)上拋,交由上游處理
    • catch異常慎王,調(diào)用interrupt()方法蚓土,恢復(fù)當(dāng)前線程的中斷狀態(tài)(調(diào)用線程的interrupt方法,需要通過中斷狀態(tài)判斷線程是否被中斷赖淤,如果不恢復(fù)北戏,就可能會(huì)導(dǎo)致狀態(tài)判斷失敗)
public class ThreadInterruptDemo {
    public static void main(String[] args) {
        Thread sleepThread1 = new Thread(()-> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        });

        Thread sleepThread2 = new Thread(()-> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //重置中斷狀態(tài)
                Thread.interrupted();
                e.printStackTrace();
            }
        });

        Thread busyThread = new Thread(()-> {
            while(true){

            }
        });
        sleepThread1.start();
        sleepThread2.start();
        busyThread.start();
        sleepThread1.interrupt();
        sleepThread2.interrupt();
        busyThread.interrupt();

        System.out.println("sleepThread1 isInterrupt = " + sleepThread1.isInterrupted());
        System.out.println("sleepThread2 isInterrupt = " + sleepThread2.isInterrupted());
        System.out.println("busyThread isInterrupt = " + busyThread.isInterrupted());
    }
}

  1. join
    join可以看作是線程間的一種協(xié)作方式漫蛔,在很多時(shí)候嗜愈,一個(gè)線程能否執(zhí)行,依賴另一個(gè)線程的執(zhí)行結(jié)果莽龟,當(dāng)A線程依賴B線程時(shí)蠕嫁,可以調(diào)用B.jion(),阻塞一直到B線程執(zhí)行完成
    join方法的核心源碼毯盈,判斷鎖等待線程isAlive剃毒,如果存活,則無限期等待搂赋,當(dāng)B線程執(zhí)行完成退出時(shí)赘阀,會(huì)調(diào)用notifyAll()方法,通知所有線程
public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        //無超時(shí)的join()脑奠,無限期等待
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            //有超時(shí)的等待
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
//java join demo
public class THreadJoinDemo {
    public static void main(String[] args) {
        Thread previousThread = Thread.currentThread();
        for (int i = 0; i < 10; i++) {
            TestJoin testJoin = new TestJoin(previousThread);
            testJoin.start();
            previousThread = testJoin;
        }
    }
}

class TestJoin extends Thread {

    Thread currentThread;

    public TestJoin(Thread currentThread) {
        this.currentThread = currentThread;
    }

    @Override
    public void run() {
        try {
            currentThread.join();
            System.out.println(currentThread.getName() + " terminated");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  1. sleep
    sleep就是按照當(dāng)前指定的時(shí)間休眠基公,時(shí)間精度取決于處理器的計(jì)時(shí)器和調(diào)度器。
    sleep對比wait
    (1) sleep是Thread類的靜態(tài)方法宋欺,wait是Object類的實(shí)例方法
    (2) sleep可以在任意地方使用轰豆;wait只能在同步代碼塊或是同步方法中使用,也就是對象已經(jīng)獲得鎖齿诞。調(diào)用wait之后酸休,會(huì)釋放鎖,線程進(jìn)入線程池祷杈,等待下一次獲取資源斑司;sleep不會(huì)釋放鎖,僅僅讓出CPU
    (3) sleep的線程在時(shí)間結(jié)束之后但汞,只需獲得CPU時(shí)間片就會(huì)繼續(xù)執(zhí)行宿刮;而wait的線程,必須等待其他線程調(diào)用notify或是notifyAll才會(huì)離開線程池特占,在獲得時(shí)間片之后才能繼續(xù)執(zhí)行

  2. yield
    yield()是Thread的靜態(tài)方法糙置,執(zhí)行之后表示該線程讓出CPU,但是讓出CPU不代表該線程就不運(yùn)行了是目,如果在下次的競爭中谤饭,該線程獲得CPU,將繼續(xù)執(zhí)行懊纳。
    yield對比sleep
    (1) 相同:都是Thread的靜態(tài)方法揉抵,執(zhí)行之后都是讓出CPU
    (2) 不同:Thread讓出CPU之后,交由其他線程去競爭嗤疯,yield讓出CPU之后冤今,交由與自己相同或是更高優(yōu)先級的線程去競爭,自己有可能繼續(xù)獲取cpu并執(zhí)行

  3. 守護(hù)線程deamon
    守護(hù)線程是一種特殊的線程茂缚,在后臺為系統(tǒng)提供服務(wù)戏罢。與之對應(yīng)的是用戶線程屋谭,只有當(dāng)最后一個(gè)用戶線程退出時(shí),守護(hù)線程才會(huì)結(jié)束龟糕,JVM也才會(huì)終止運(yùn)行桐磁。
    注意:
    在deamon線程退出時(shí),并不會(huì)執(zhí)行finally代碼塊
    在deamon線程退出時(shí)讲岁,并不會(huì)執(zhí)行finally代碼塊
    在deamon線程退出時(shí)我擂,并不會(huì)執(zhí)行finally代碼塊

public class DeamonThreadDemo {

    public static void main(String[] args) {
        Thread deamonThread = new Thread(()->{
            while (true) {
                System.out.println("i am alive");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    System.out.println("i am finally quit");
                }
            }
        });

        deamonThread.setDaemon(true);
        deamonThread.start();

        try {
            Thread.sleep(800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

demo中,設(shè)置了守護(hù)線程deamonThread缓艳,主線程sleep(800)校摩,會(huì)讓守護(hù)線程獲得一次執(zhí)行機(jī)會(huì),打印一次“i am alive”和“i am finally quit”阶淘,主線程sleep(500)之后衙吩,守護(hù)線程繼續(xù)執(zhí)行一次“i am alive”,接著主線程退出舶治,守護(hù)線程也跟著退出分井,并沒有執(zhí)行finally代碼塊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霉猛,一起剝皮案震驚了整個(gè)濱河市尺锚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惜浅,老刑警劉巖瘫辩,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坛悉,居然都是意外死亡伐厌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進(jìn)店門裸影,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挣轨,“玉大人,你說我怎么就攤上這事轩猩【戆纾” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵均践,是天一觀的道長晤锹。 經(jīng)常有香客問我,道長彤委,這世上最難降的妖魔是什么鞭铆? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮焦影,結(jié)果婚禮上车遂,老公的妹妹穿的比我還像新娘封断。我一直安慰自己,他們只是感情好艰额,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布澄港。 她就那樣靜靜地躺著,像睡著了一般柄沮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上废岂,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天祖搓,我揣著相機(jī)與錄音,去河邊找鬼湖苞。 笑死拯欧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的财骨。 我是一名探鬼主播镐作,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼隆箩!你這毒婦竟也來了该贾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤捌臊,失蹤者是張志新(化名)和其女友劉穎杨蛋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體理澎,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逞力,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了糠爬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寇荧。...
    茶點(diǎn)故事閱讀 40,928評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖执隧,靈堂內(nèi)的尸體忽然破棺而出揩抡,到底是詐尸還是另有隱情,我是刑警寧澤殴玛,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布捅膘,位于F島的核電站,受9級特大地震影響滚粟,放射性物質(zhì)發(fā)生泄漏寻仗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一凡壤、第九天 我趴在偏房一處隱蔽的房頂上張望署尤。 院中可真熱鬧耙替,春花似錦、人聲如沸曹体。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽箕别。三九已至铜幽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間串稀,已是汗流浹背除抛。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留母截,地道東北人到忽。 一個(gè)月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像清寇,于是被迫代替她去往敵國和親喘漏。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評論 2 361

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

  • 0 前言 在過去單CPU時(shí)代华烟,單任務(wù)在一個(gè)時(shí)間點(diǎn)只能執(zhí)行單一程序翩迈。之后發(fā)展到多任務(wù)階段,計(jì)算機(jī)能在同一時(shí)間點(diǎn)并行執(zhí)...
    七寸知架構(gòu)閱讀 9,939評論 6 95
  • Java自誕生開始就明智地選擇了內(nèi)置對多線程的支持垦江,這使得Java語言相比同一時(shí)期的其他語言具有明顯的優(yōu)勢帽馋。線程作...
    shallowinggg閱讀 495評論 0 2
  • 我是在深入學(xué)習(xí) kotlin 時(shí)第一次看到協(xié)程,作為傳統(tǒng)線程模型的進(jìn)化版比吭,雖說協(xié)程這個(gè)概念幾十年前就有了绽族,但是協(xié)程...
    前行的烏龜閱讀 99,897評論 32 182
  • 第1章 并發(fā)編程的挑戰(zhàn) 1.1 上下文切換 即便是單核CPU也支持多線程并發(fā),CPU通過給每個(gè)線程分配時(shí)間片(幾十...
    卑鄙的鹿尤菌閱讀 4,783評論 1 22
  • 必備的理論基礎(chǔ) 1.操作系統(tǒng)作用: 隱藏丑陋復(fù)雜的硬件接口衩藤,提供良好的抽象接口吧慢。 管理調(diào)度進(jìn)程,并將多個(gè)進(jìn)程對硬件...
    drfung閱讀 3,548評論 0 5