多線程系列第(七)篇---線程池

技術(shù)背景

在面向?qū)ο缶幊讨校瑒?chuàng)建和銷毀對象是很費時間的棍丐,因為創(chuàng)建一個對象要獲取內(nèi)存資源或者其它更多資源误辑。在Java中更是如此,虛擬機將試圖跟蹤每一個對象歌逢,以便能夠在對象銷毀后進行垃圾回收巾钉。所以提高服務(wù)程序效率的一個手段就是盡可能減少創(chuàng)建和銷毀對象的次數(shù),特別是一些很耗資源的對象創(chuàng)建和銷毀秘案。如何利用已有對象來服務(wù)就是一個需要解決的關(guān)鍵問題砰苍,其實這就是一些"池化資源"技術(shù)產(chǎn)生的原因。比如大家所熟悉的數(shù)據(jù)庫連接池正是遵循這一思想而產(chǎn)生的阱高,本文將介紹的線程池技術(shù)同樣符合這一思想赚导。
--------------以上內(nèi)容來自百度百科-----------------------

java提供的幾種線程池

先介紹幾個類

Executors
提供一系列靜態(tài)方法用于創(chuàng)建線程池,返回的線程池對象都繼承了ExecutorService接口

ExecutorService
線程池對象赤惊,用于管理線程池中的線程

ScheduledExecutorService
具有定時效果的線程池對象

線程池介紹

newCachedThreadPool()
創(chuàng)建一個彈性線程池吼旧,自動回收不使用的線程(終止并從緩存中移除那些已有 60 秒鐘未被使用的線程),(在無可用線程的情況下)自動的為新來的task創(chuàng)建新線程荐捻。

newSingleThreadExecutor()
創(chuàng)建一個只有一個線程的線程池黍少,一個任務(wù)執(zhí)行完畢,下一個任務(wù)才能接著執(zhí)行

newFixedThreadPool(int nThreads)
創(chuàng)建一個固定線程數(shù)量的線程池处面,每次提交一個任務(wù)就創(chuàng)建一個線程厂置,直到線程達(dá)到線程池的最大大小。

newScheduledThreadPool(int corePoolSize)
創(chuàng)建一個定長線程池魂角,支持定時及周期性任務(wù)執(zhí)行昵济。注意corePoolSize,當(dāng)需要的線程超過corePoolSize時,還是會新建線程访忿,只不過超過的部分會在空閑時間回收

代碼示例

CachedThreadPool代碼示例
public class ThreadPool_CacheDemo {

public static void main(String[] args) {

    ExecutorService es1 = Executors.newCachedThreadPool();
    
    es1.execute(new MyTask("任務(wù)1"));
    es1.execute(new MyTask("任務(wù)2"));
    es1.execute(new MyTask("任務(wù)3"));
    es1.execute(new MyTask("任務(wù)4"));
    es1.execute(new MyTask("任務(wù)5"));

  }

}

class MyTask implements Runnable {
  private String taskName;

  public MyTask(String taskName){
    this.taskName=taskName;
}

public void run() {
    System.out.println(taskName+"執(zhí)行時間:"+System.currentTimeMillis());
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
  }
}

運行結(jié)果
任務(wù)4執(zhí)行時間:1507874348487  線程名:pool-1-thread-4
任務(wù)3執(zhí)行時間:1507874348487  線程名:pool-1-thread-3
任務(wù)1執(zhí)行時間:1507874348487  線程名:pool-1-thread-1
任務(wù)5執(zhí)行時間:1507874348487  線程名:pool-1-thread-5
任務(wù)2執(zhí)行時間:1507874348487  線程名:pool-1-thread-2

可以看出5個任務(wù)幾乎是同一時間執(zhí)行的瞧栗,并且每個任務(wù)都對應(yīng)一個線程

SingleThreadPool代碼示例
public class ThreadPool_SingleDemo {

    public static void main(String[] args) {

    ExecutorService es2 = Executors.newSingleThreadExecutor();
    
    es2.execute(new MyTask("任務(wù)1"));
    es2.execute(new MyTask("任務(wù)2"));
    es2.execute(new MyTask("任務(wù)3"));
    es2.execute(new MyTask("任務(wù)4"));
    es2.execute(new MyTask("任務(wù)5"));
  }
}

運行結(jié)果
任務(wù)1執(zhí)行時間:1507874414908  線程名:pool-1-thread-1
任務(wù)2執(zhí)行時間:1507874416909  線程名:pool-1-thread-1
任務(wù)3執(zhí)行時間:1507874418910  線程名:pool-1-thread-1
任務(wù)4執(zhí)行時間:1507874420912  線程名:pool-1-thread-1
任務(wù)5執(zhí)行時間:1507874422913  線程名:pool-1-thread-1

可以看出每個任務(wù)和上個任務(wù)執(zhí)行時間相差差不多2秒鐘,并且只有一個線程

FixedThreadPool代碼示例
public class ThreadPool_FixedDemo {

public static void main(String[] args) {
    
    ExecutorService es3 = Executors.newFixedThreadPool(2);
    
    es3.execute(new MyTask("任務(wù)1"));
    es3.execute(new MyTask("任務(wù)2"));
    es3.execute(new MyTask("任務(wù)3"));
    es3.execute(new MyTask("任務(wù)4"));
    es3.execute(new MyTask("任務(wù)5"));
  }
}

運行結(jié)果
任務(wù)2執(zhí)行時間:1507874489559  線程名:pool-1-thread-2
任務(wù)1執(zhí)行時間:1507874489559  線程名:pool-1-thread-1
任務(wù)3執(zhí)行時間:1507874491565  線程名:pool-1-thread-2
任務(wù)4執(zhí)行時間:1507874491565  線程名:pool-1-thread-1
任務(wù)5執(zhí)行時間:1507874493569  線程名:pool-1-thread-2

可以看出海铆,每兩個任務(wù)的執(zhí)行時間是相同的迹恐,并且只有兩個線程

ScheduledThreadPool代碼示例

注意,ScheduledThreadPool也可以調(diào)用execute方法卧斟,當(dāng)然如果調(diào)用這個方法就和前面介紹的FixedThreadPool沒什么區(qū)別了

public class ThreadPool_ScheduledDemo {

public static void main(String[] args) {

    ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);

    System.out.println("線程池任務(wù)schedule時間:" + System.currentTimeMillis());
    ses.schedule(new MyTask("任務(wù)1"), 2, TimeUnit.SECONDS);
    ses.scheduleAtFixedRate(new MyTask("任務(wù)2"), 2, 3, TimeUnit.SECONDS);     
    ses.scheduleWithFixedDelay(new MyTask("任務(wù)3"), 1, 3, TimeUnit.SECONDS);
  }
}

可以看到上述程序有3種執(zhí)行任務(wù)的方法,下面將一一介紹

ses.schedule(new MyTask("任務(wù)1"), 2, TimeUnit.SECONDS);
運行結(jié)果
線程池任務(wù)schedule時間:1507876230078
任務(wù)1執(zhí)行時間:1507876232081  線程名:pool-1-thread-1

可以看出任務(wù)1在延遲了2秒后才去執(zhí)行任務(wù)

ses.scheduleAtFixedRate(new MyTask("任務(wù)2"), 2, 3, TimeUnit.SECONDS);
運行結(jié)果
線程池任務(wù)schedule時間:1507876503620
任務(wù)2執(zhí)行時間:1507876505627  線程名:pool-1-thread-1
任務(wù)2執(zhí)行時間:1507876508622  線程名:pool-1-thread-1
任務(wù)2執(zhí)行時間:1507876511627  線程名:pool-1-thread-1
任務(wù)2執(zhí)行時間:1507876514627  線程名:pool-1-thread-1
任務(wù)2執(zhí)行時間:1507876517627  線程名:pool-1-thread-1
任務(wù)2執(zhí)行時間:1507876520627  線程名:pool-1-thread-1
任務(wù)2執(zhí)行時間:1507876523627  線程名:pool-1-thread-1
任務(wù)2執(zhí)行時間:1507876526627  線程名:pool-1-thread-1

可以看出在延遲2秒后锤岸,任務(wù)2在周期性的執(zhí)行著是偷,從時間上看這個周期剛好是3秒

ses.scheduleWithFixedDelay(new MyTask("任務(wù)3"), 1, 3, TimeUnit.SECONDS);
運行結(jié)果
線程池任務(wù)schedule時間:1507876760966
任務(wù)3執(zhí)行時間:1507876761969  線程名:pool-1-thread-1
任務(wù)3執(zhí)行時間:1507876766972  線程名:pool-1-thread-1
任務(wù)3執(zhí)行時間:1507876771974  線程名:pool-1-thread-1
任務(wù)3執(zhí)行時間:1507876776979  線程名:pool-1-thread-1
任務(wù)3執(zhí)行時間:1507876781985  線程名:pool-1-thread-1
任務(wù)3執(zhí)行時間:1507876786990  線程名:pool-1-thread-1
任務(wù)3執(zhí)行時間:1507876792000  線程名:pool-1-thread-1

可以看出在延遲1秒后蛋铆,任務(wù)2在周期性的執(zhí)行著戒职,從時間上看這個周期剛好是5秒透乾,為什么是5秒了,因為任務(wù)的執(zhí)行時間為2秒捧韵,在加上設(shè)置的3秒汉操,總共就是5秒了

線程池的關(guān)閉

線程池的有兩個關(guān)閉方法磷瘤,shutdown和shutdownNow

shutdown的作用
關(guān)閉線程池,不再接收新的線程,但線程池中已有的線程任務(wù)會執(zhí)行完畢

shutdownNow
關(guān)閉線程池针炉,不再接受線的線程篡帕,且線程池中已經(jīng)有的線程的任務(wù)也會被立刻終止

demo示例

public class ThreadPool_ScheduledDemo {

  public static void main(String[] args) {

    ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);

    System.out.println("線程池任務(wù)schedule時間:" + System.currentTimeMillis());
    // ses.schedule(new MyTask("任務(wù)1"), 2, TimeUnit.SECONDS);
    // ses.scheduleAtFixedRate(new MyTask("任務(wù)2"), 2, 3, TimeUnit.SECONDS);
    //ses.scheduleWithFixedDelay(new MyTask("任務(wù)3"), 1, 3, TimeUnit.SECONDS);
    
    ses.scheduleWithFixedDelay(new MyTask1("任務(wù)4"), 1, 3, TimeUnit.SECONDS);

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    ses.shutdown(); //關(guān)閉線程池,不在接收新的線程,但線程池中已有的線程任務(wù)會執(zhí)行完畢
//  ses.shutdownNow(); // 關(guān)閉線程池拢军,不再接受線的線程茉唉,且線程池中已經(jīng)有的線程的任務(wù)也會被立刻終止
  }
}

class MyTask1 implements Runnable {
  private String taskName;

  public MyTask1(String taskName) {
    this.taskName = taskName;
}

  public void run() {
    System.out.println(taskName + "開始時間:" + System.currentTimeMillis());
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        System.out.println(taskName+"被中斷");
    }
    System.out.println(taskName + "結(jié)束時間:" + System.currentTimeMillis());
  }
}

調(diào)用shutdown方法

運行結(jié)果
線程池任務(wù)schedule時間:1507877541748
任務(wù)4開始時間:1507877542756
任務(wù)4結(jié)束時間:1507877545757

調(diào)用shutdownNow方法

運行結(jié)果
線程池任務(wù)schedule時間:1507877802636
任務(wù)4開始時間:1507877803642
任務(wù)4被中斷
任務(wù)4結(jié)束時間:1507877804641

結(jié)果分析
本來是周期性的任務(wù)赌渣,調(diào)用關(guān)閉方法后,線程池中的任務(wù)就不會在執(zhí)行了
任務(wù)4览芳,在延遲1秒后開始執(zhí)行
任務(wù)內(nèi)部執(zhí)行時間為3秒
主線程會在延遲2秒后關(guān)閉線程池
shutdown方法沧竟,結(jié)束時間-開始時間==3秒,說明的確是在任務(wù)執(zhí)行完畢后才關(guān)閉的
shutdownNow方法杈笔,結(jié)束時間-開始時間==1秒蒙具,說明任務(wù)還沒結(jié)束就已經(jīng)被中斷了

結(jié)論朽肥,推薦使用shutdown

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末衡招,一起剝皮案震驚了整個濱河市始腾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌穗椅,老刑警劉巖房待,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拜鹤,居然都是意外死亡流椒,警方通過查閱死者的電腦和手機宣虾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門绣硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人握玛,你說我怎么就攤上這事挠铲〖庞眨” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵瓢棒,是天一觀的道長音羞。 經(jīng)常有香客問我嗅绰,道長搀继,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任财边,我火速辦了婚禮酣难,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘紧索。我一直安慰自己菜谣,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布媳危。 她就那樣靜靜地躺著待笑,像睡著了一般抓谴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天措拇,我揣著相機與錄音慎宾,去河邊找鬼。 笑死券犁,一個胖子當(dāng)著我的面吹牛汹碱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咳促,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼褂删,長吁一口氣:“原來是場噩夢啊……” “哼冲茸!你這毒婦竟也來了缅帘?” 一聲冷哼從身側(cè)響起难衰,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤召衔,失蹤者是張志新(化名)和其女友劉穎作谭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體醇蝴,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年霉涨,在試婚紗的時候發(fā)現(xiàn)自己被綠了笙瑟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片癞志。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖错洁,靈堂內(nèi)的尸體忽然破棺而出戒突,到底是詐尸還是另有隱情,我是刑警寧澤导而,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布嗡载,位于F島的核電站仍稀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏遥巴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一拾弃、第九天 我趴在偏房一處隱蔽的房頂上張望摆霉。 院中可真熱鬧,春花似錦搭盾、人聲如沸婉支。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽跟畅。三九已至溶推,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悼潭,已是汗流浹背舞箍。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工疏橄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晃酒。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓贝次,卻偏偏與公主長得像彰导,于是被迫代替她去往敵國和親敲茄。 傳聞我的和親對象是個殘疾皇子堰燎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

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