01. 就該這么學(xué)并發(fā) - 線程的創(chuàng)建(多姿勢(shì))

本章開(kāi)始,咸魚君將開(kāi)啟一個(gè)新的專欄 就該這么學(xué)并發(fā) 來(lái)記錄學(xué)習(xí)Java并發(fā)編程的相關(guān)內(nèi)容~

互聯(lián)網(wǎng)圖片,版權(quán)歸原作者

前言

說(shuō)到并發(fā)編程,就不得不先說(shuō)多線程,因?yàn)?strong>并發(fā)的本質(zhì)就是多線程的處理

進(jìn)程和線程

  • 進(jìn)程 (ps: 每一個(gè)程序就是一個(gè)進(jìn)程)
進(jìn)程指運(yùn)行中的應(yīng)用程序,每個(gè)程序運(yùn)行時(shí)就會(huì)開(kāi)啟一個(gè)進(jìn)程
進(jìn)程是系統(tǒng)分配資源的最小單位
進(jìn)程有獨(dú)立系統(tǒng)資源(進(jìn)程間的資源是隔離的)
每個(gè)進(jìn)程至少有一個(gè)線程
  • 線程 (ps: 進(jìn)程至少有一個(gè)線程)
線程依托于進(jìn)程
線程除了支持本身運(yùn)行的一點(diǎn)系統(tǒng)資源外(如: 
控制線程運(yùn)行的線程控制塊,保留局部變量和少數(shù)參數(shù)的棾茫空間)
不額外擁有獨(dú)立的資源,但是共享進(jìn)程的資源
線程也是CPU調(diào)度的最小單位

我們眼見(jiàn)為實(shí),右鍵打開(kāi)windows系統(tǒng)的任務(wù)管理器

image

CPU調(diào)度執(zhí)行策略簡(jiǎn)介

CPU如何調(diào)度進(jìn)程/線程呢?

CPU依托于CPU調(diào)度算法來(lái)調(diào)度,如下

  • RR(Round Robin)
時(shí)間片輪轉(zhuǎn)調(diào)度算法,每個(gè)進(jìn)程/線程會(huì)被分配一個(gè)時(shí)間片,
在這個(gè)時(shí)間片的時(shí)間段內(nèi),允許進(jìn)程/線程運(yùn)行
  • FIFO((First In First Out)
先進(jìn)先出算法,按照進(jìn)程就緒的先后順序來(lái)使用CPU
  • SCBF(Shortest CPU Burst First)
短進(jìn)程優(yōu)先算法,先給每個(gè)進(jìn)程都設(shè)置一個(gè)優(yōu)先級(jí),
根據(jù)比較優(yōu)先級(jí)來(lái)確定下一個(gè)執(zhí)行的進(jìn)程;
將一些相對(duì)短的進(jìn)程優(yōu)先級(jí)適當(dāng)提高
  • Virtual RR
虛擬輪轉(zhuǎn)法,時(shí)間片輪轉(zhuǎn)法的改進(jìn)算法
  • PSA(Priority Scheduling Algorithm)
優(yōu)先級(jí)算法,給每一個(gè)進(jìn)程一個(gè)優(yōu)先級(jí),
優(yōu)先級(jí)越高的事件先執(zhí)行
  • 其它……

看完以上調(diào)度算法, 大家留下個(gè)

CPU有很多調(diào)度算法,并不僅僅只有RR時(shí)間片調(diào)度

這么個(gè)印象就好

因?yàn)楝F(xiàn)在說(shuō)起線程調(diào)度, 很多文章開(kāi)口就是時(shí)間片調(diào)度,可能會(huì)造成誤解, 認(rèn)為CPU調(diào)度只有RR算法一種, 這顯然是不嚴(yán)謹(jǐn)的.

JVM虛擬機(jī)簡(jiǎn)介

JVM是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的.

引入Java語(yǔ)言虛擬機(jī)后,Java語(yǔ)言在不同平臺(tái)上運(yùn)行時(shí)不需要重新編譯.Java語(yǔ)言使用Java虛擬機(jī)屏蔽了與具體平臺(tái)相關(guān)的信息,使得Java語(yǔ)言只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼,就可以在多種平臺(tái)上不加修改地運(yùn)行

簡(jiǎn)而言之

JVM就是個(gè)翻譯器,依托于它,java才實(shí)現(xiàn)了 “一次編譯,多處(平臺(tái))運(yùn)行!”

當(dāng)然,和C,C++等編譯語(yǔ)言不同的點(diǎn)在于,java編譯出來(lái)的是字節(jié)碼文件,也就是“.class”文件,這種文件被JVM所認(rèn)識(shí),再由JVM來(lái)翻譯解釋成具體平臺(tái)的可執(zhí)行文件,而后可被具體平臺(tái)運(yùn)行.

簡(jiǎn)單的流程關(guān)系如下:

java文件(.java)--編譯-->(.class)---->(JVM)解釋---->(可執(zhí)行文件)運(yùn)行

所以

有人說(shuō)JAVA是編譯型語(yǔ)言,也有人說(shuō)是解釋型語(yǔ)言,其實(shí)都沒(méi)什么問(wèn)題!

線程的生命周期

下面我們進(jìn)入本章的正題,著重講解下線程的生命周期!

先來(lái)張線程生命周期的綱要圖,方便大家理解,后續(xù)針對(duì)每個(gè)節(jié)點(diǎn)細(xì)講.

圖片來(lái)自互聯(lián)網(wǎng),版權(quán)歸原作者

線程大致有五種狀態(tài)

本章, 我們聊聊線程生命周期的第一個(gè)狀態(tài)“新建”.

新建(New)

當(dāng)線程對(duì)象對(duì)創(chuàng)建后,即進(jìn)入了新建狀態(tài)

- 此時(shí)JVM為其分配內(nèi)存,并初始化其成員變量的值袁余;

- 此時(shí)線程對(duì)象沒(méi)有表現(xiàn)出任何線程的動(dòng)態(tài)特征,程序也不會(huì)執(zhí)行線程的線程執(zhí)行體run()封寞;

線程的創(chuàng)建分為

  • 無(wú)返回值

  • 有返回值

下面我們一一示例

無(wú)返回值的創(chuàng)建方式

  • 繼承Thread類
class ExtendsThread extends Thread {
    // 重寫run方法
    @Override
    public void run() {
        System.out.println("子線程" + Thread.currentThread().getName() + "正在執(zhí)行 ");
    }
}

class Test {
    public static void main(String[] args) throws Exception {
        ExtendsThread t = new ExtendsThread();
        t.start();
}

當(dāng)然,我們也可以簡(jiǎn)單點(diǎn),使用匿名類的方式創(chuàng)建

class Test {
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(){
               public void run(){
                   System.out.println("Thread Running");
               }  
             };
        t.start();
}
  • 實(shí)現(xiàn)Runnable接口
public class ImpRunnableThread implements Runnable {
 //重寫run方法 
  @Override
  public void run(){
  log.info("子線程{}正在執(zhí)行 ",Thread.currentThread().getName());
  }
}

//測(cè)試
class Test {
    public void main(String[] args){
        ImpRunnableThread t = new ImpRunnableThread();
        Thread thread = new Thread(t);
        thread.start();
    } 
}

同樣,我們可以使用匿名類的方式創(chuàng)建

public class Test {
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("Runnable running");
            }
        });
        t.start();
    }
}

有返回值的創(chuàng)建方式

  • 使用Callable和Future創(chuàng)建線程
public class ImpCallableThread implements Callable {
    //重寫call方法
    @Override
    public String call() {
        log.info("子線程{}正在執(zhí)行 ",Thread.currentThread().getName());
        return "返回值";
    }
}
//測(cè)試
class Test {
    public void main(String[] args){
        FutureTask task = new FutureTask(new  ImpCallableThread());
        new Thread(task).run();
        log.info("線程中獲取到的返回信息  {} ",task.get());
}

線程池創(chuàng)建線程

其實(shí)是比較推薦大家用線程池的方式去創(chuàng)建線程的,好處如下

  • 降低資源消耗
通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗
  • 提高響應(yīng)速度
當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行
  • 提高線程的可管理性
線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控

關(guān)于如何創(chuàng)建線程池,其實(shí)Java 已經(jīng)提供了 Executors,

Java 通過(guò) Executors 提供四種線程池,例如:

  • newCachedThreadPool
創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,
可靈活回收空閑線程,若無(wú)可回收,則新建線程
  • newFixedThreadPool
創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),
超出的線程會(huì)在隊(duì)列中等待
  • newScheduledThreadPool
創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行
  • newSingleThreadExecutor
創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),
保證所有任務(wù)按照指定順序(FIFO,LIFO,優(yōu)先級(jí))執(zhí)行

不過(guò),從實(shí)際開(kāi)發(fā)角度,直接使用Executors返回線程池會(huì)有一些問(wèn)題,所以我們并不推薦以上的方式來(lái)創(chuàng)建線程池(阿里內(nèi)部也是明確規(guī)定不允許采用這種方式的)

原因如下:

  • FixedThreadPool 和 SingleThreadExecutor : 允許請(qǐng)求的隊(duì)列長(zhǎng)度為 Integer.MAX_VALUE,可能堆積大量的請(qǐng)求,從而導(dǎo)致OOM

  • CachedThreadPool 和 ScheduledThreadPool : 允許創(chuàng)建的線程數(shù)量為 Integer.MAX_VALUE ,可能會(huì)創(chuàng)建大量線程,從而導(dǎo)致OOM

避免使用Executors創(chuàng)建線程池主要是為了避免其中的默認(rèn)實(shí)現(xiàn),可以改用ThreadPoolExecutor構(gòu)造方法指定參數(shù)

image.png
class Test {
public void main(String[] args){
  ExecutorService executorService = new ThreadPoolExecutor(5,10,10,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(5));
  for (int i = 0; i < 20; i++) {
       executorService.execute(new Runnable() {
       @Override
       public void run() {
               log.info("線程  {}  開(kāi)始",Thread.currentThread().getName());
         }});
       }
}
}

需要指定核心線程池的大小最大線程池的數(shù)量节预、保持存活的時(shí)間叶摄、等待隊(duì)列容量的大小;

在這種情況下一旦提交的線程數(shù)超過(guò)當(dāng)前可用的線程數(shù)時(shí)就會(huì)拋出拒絕執(zhí)行的異常

java.util.concurrent.RejectedExecutionException 

解決方式就是盡量調(diào)大最大線程數(shù)

除了自己定義的ThreadPool之外,還可以使用開(kāi)源庫(kù) apache guava等,大家自行選擇如何使用~

哪種創(chuàng)建線程的方式比較好?

關(guān)于這個(gè)問(wèn)題,其實(shí)推薦的是

實(shí)現(xiàn)Runnable接口

因?yàn)?strong>線程池可以有效的管理實(shí)現(xiàn)了Runnable接口的線程,如果線程池滿了,新的線程就會(huì)排隊(duì)等候執(zhí)行,直到線程池空閑出來(lái)為止.而如果線程是通過(guò)實(shí)現(xiàn)Thread子類實(shí)現(xiàn)的,這將會(huì)復(fù)雜一些.

有時(shí)我們要同時(shí)融合實(shí)現(xiàn)Runnable接口和Thread子類兩種方式.例如,實(shí)現(xiàn)了Thread子類的實(shí)例可以執(zhí)行多個(gè)實(shí)現(xiàn)了Runnable接口的線程.一個(gè)典型的應(yīng)用就是線程池.

歡迎關(guān)注我

技術(shù)公眾號(hào) “CTO技術(shù)”

訂閱號(hào).png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市安拟,隨后出現(xiàn)的幾起案子蛤吓,更是在濱河造成了極大的恐慌,老刑警劉巖糠赦,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件会傲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拙泽,警方通過(guò)查閱死者的電腦和手機(jī)唆铐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)奔滑,“玉大人艾岂,你說(shuō)我怎么就攤上這事∨笃洌” “怎么了王浴?”我有些...
    開(kāi)封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵脆炎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我氓辣,道長(zhǎng)秒裕,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任钞啸,我火速辦了婚禮几蜻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘体斩。我一直安慰自己梭稚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布絮吵。 她就那樣靜靜地躺著弧烤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蹬敲。 梳的紋絲不亂的頭發(fā)上暇昂,一...
    開(kāi)封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音伴嗡,去河邊找鬼急波。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瘪校,可吹牛的內(nèi)容都是我干的幔崖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼渣淤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了吉嫩?” 一聲冷哼從身側(cè)響起价认,我...
    開(kāi)封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎自娩,沒(méi)想到半個(gè)月后用踩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忙迁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年脐彩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姊扔。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惠奸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恰梢,到底是詐尸還是另有隱情佛南,我是刑警寧澤梗掰,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站嗅回,受9級(jí)特大地震影響及穗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绵载,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一埂陆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧娃豹,春花似錦焚虱、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至定续,卻和暖如春谍咆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背私股。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工摹察, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人倡鲸。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓供嚎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親峭状。 傳聞我的和親對(duì)象是個(gè)殘疾皇子克滴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348