Java多線程實(shí)現(xiàn)方式

??jdk1.5之后加入了java.util.concurrent包,這個(gè)包主要介紹java中線程和線程池的使用敷燎。

Java多線程實(shí)現(xiàn)的四種方式

Java多線程實(shí)現(xiàn)方式主要有四種:繼承Thread類玻墅、實(shí)現(xiàn)Runnable接口够庙、實(shí)現(xiàn)Callable接口通過(guò)FutureTask包裝器來(lái)創(chuàng)建Thread線程伍伤、使用ExecutorService乳蛾、Callable抛杨、Future實(shí)現(xiàn)有返回結(jié)果的多線程够委。其中前兩種方式線程執(zhí)行完后都沒(méi)有返回值,后兩種是帶返回值的蝶桶。

1. 繼承Thread類創(chuàng)建線程

Thread類本質(zhì)上是實(shí)現(xiàn)了Runnable接口的一個(gè)實(shí)例慨绳,代表一個(gè)線程的實(shí)例。啟動(dòng)線程的唯一方法就是通過(guò)Thread類的start()實(shí)例方法真竖。start()方法是一個(gè)native方法脐雪,它將啟動(dòng)一個(gè)新線程,并執(zhí)行run()方法恢共。這種方式實(shí)現(xiàn)多線程很簡(jiǎn)單战秋,通過(guò)自己的類直接extend Thread,并復(fù)寫(xiě)run()方法讨韭,就可以啟動(dòng)新線程并執(zhí)行自己定義的run()方法脂信。例如:

public class MyThread extends Thread {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
}
MyThread myThread1 = new MyThread();  
MyThread myThread2 = new MyThread();  
myThread1.start();  
myThread2.start();  

2. 實(shí)現(xiàn)Runnable接口創(chuàng)建線程

如果自己的類已經(jīng)extends另一個(gè)類癣蟋,就無(wú)法直接extends Thread,此時(shí)狰闪,可以實(shí)現(xiàn)一個(gè)Runnable接口疯搅,如下:
為了啟動(dòng)MyThread,需要首先實(shí)例化一個(gè)Thread埋泵,并傳入自己的MyThread實(shí)例:
事實(shí)上幔欧,當(dāng)傳入一個(gè)Runnable target參數(shù)給Thread后,Thread的run()方法就會(huì)調(diào)用target.run()丽声,參考JDK源代碼:

3. 實(shí)現(xiàn)Callable接口通過(guò)FutureTask包裝器來(lái)創(chuàng)建Thread線程

Callable接口(也只有一個(gè)方法)定義如下:

public interface Callable<V>   { V call() throws Exception;}
public class SomeCallable<V> extends OtherClass implements Callable<V> {
    @Override
    public V call() throws Exception {
        // TODO Auto-generated method stub
        return null;
    }
}

Callable<V> oneCallable = new SomeCallable<V>();
//由Callable<Integer>創(chuàng)建一個(gè)FutureTask<Integer>對(duì)象
FutureTask<V> oneTask = new FutureTask<V>(oneCallable);
//注釋:FutureTask<Integer>是一個(gè)包裝器礁蔗,它通過(guò)接受Callable<Integer>來(lái)創(chuàng)建,它同時(shí)實(shí)現(xiàn)了Future和Runnable接口雁社。
  //由FutureTask<Integer>創(chuàng)建一個(gè)Thread對(duì)象:
Thread oneThread = new Thread(oneTask);
oneThread.start();
//至此浴井,一個(gè)線程就創(chuàng)建完成了。

4. 使用ExecutorService霉撵、Callable磺浙、Future實(shí)現(xiàn)有返回結(jié)果的線

ExecutorService、Callable徒坡、Future三個(gè)接口實(shí)際上都是屬于Executor框架屠缭。返回結(jié)果的線程是在JDK1.5中引入的新特征,有了這種特征就不需要再為了得到返回值而大費(fèi)周折了崭参。而且自己實(shí)現(xiàn)了也可能漏洞百出。
可返回值的任務(wù)必須實(shí)現(xiàn)Callable接口款咖。類似的何暮,無(wú)返回值的任務(wù)必須實(shí)現(xiàn)Runnable接口。
執(zhí)行Callable任務(wù)后铐殃,可以獲取一個(gè)Future的對(duì)象海洼,在該對(duì)象上調(diào)用get就可以獲取到Callable任務(wù)返回的Object了。
注意:get方法是阻塞的富腊,即:線程無(wú)返回結(jié)果坏逢,get方法會(huì)一直等待。
再結(jié)合線程池接口ExecutorService就可以實(shí)現(xiàn)傳說(shuō)中有返回結(jié)果的多線程了赘被。
下面提供了一個(gè)完整的有返回結(jié)果的多線程測(cè)試?yán)邮钦贘DK1.5下驗(yàn)證過(guò)沒(méi)問(wèn)題可以直接使用。代碼如下:

import java.util.concurrent.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;

/**
* 有返回值的線程
*/
@SuppressWarnings("unchecked")
public class Test {
public static void main(String[] args) throws ExecutionException,
    InterruptedException {
   System.out.println("----程序開(kāi)始運(yùn)行----");
   Date date1 = new Date();
   int taskSize = 5;
   // 創(chuàng)建一個(gè)線程池
   ExecutorService pool = Executors.newFixedThreadPool(taskSize);
   // 創(chuàng)建多個(gè)有返回值的任務(wù)
   List<Future> list = new ArrayList<Future>();
   for (int i = 0; i < taskSize; i++) {
    Callable c = new MyCallable(i + " ");
    // 執(zhí)行任務(wù)并獲取Future對(duì)象
    Future f = pool.submit(c);
    // System.out.println(">>>" + f.get().toString());  
    list.add(f);
   }
   // 關(guān)閉線程池
   pool.shutdown();

   // 獲取所有并發(fā)任務(wù)的運(yùn)行結(jié)果
   for (Future f : list) {
    // 從Future對(duì)象上獲取任務(wù)的返回值民假,并輸出到控制臺(tái)
    System.out.println(">>>" + f.get().toString());
   }

   Date date2 = new Date();
   System.out.println("----程序結(jié)束運(yùn)行----浮入,程序運(yùn)行時(shí)間【"  
     + (date2.getTime() - date1.getTime()) + "毫秒】");  
}
}

class MyCallable implements Callable<Object> {  
private String taskNum;  

MyCallable(String taskNum) {  
   this.taskNum = taskNum;  
}  

public Object call() throws Exception {
   System.out.println(">>>" + taskNum + "任務(wù)啟動(dòng)");
   Date dateTmp1 = new Date();
   Thread.sleep(1000);
   Date dateTmp2 = new Date();
   long time = dateTmp2.getTime() - dateTmp1.getTime();
   System.out.println(">>>" + taskNum + "任務(wù)終止");
   return taskNum + "任務(wù)返回運(yùn)行結(jié)果,當(dāng)前任務(wù)時(shí)間【" + time + "毫秒】";
}
}

代碼說(shuō)明:
上述代碼中Executors類,提供了一系列工廠方法用于創(chuàng)建線程池羊异,返回的線程池都實(shí)現(xiàn)了ExecutorService接口事秀。
public static ExecutorService newFixedThreadPool(int nThreads)
創(chuàng)建固定數(shù)目線程的線程池彤断。
public static ExecutorService newCachedThreadPool()
創(chuàng)建一個(gè)可緩存的線程池,調(diào)用execute 將重用以前構(gòu)造的線程(如果線程可用)易迹。如果現(xiàn)有線程沒(méi)有可用的宰衙,則創(chuàng)建一個(gè)新線程并添加到池中。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程睹欲。
public static ExecutorService newSingleThreadExecutor()
創(chuàng)建一個(gè)單線程化的Executor供炼。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
創(chuàng)建一個(gè)支持定時(shí)及周期性的任務(wù)執(zhí)行的線程池,多數(shù)情況下可用來(lái)替代Timer類句伶。
ExecutoreService提供了submit()方法劲蜻,傳遞一個(gè)Callable,或Runnable考余,返回Future先嬉。如果Executor后臺(tái)線程池還沒(méi)有完成Callable的計(jì)算,這調(diào)用返回Future對(duì)象的get()方法楚堤,會(huì)阻塞直到計(jì)算完成疫蔓。



  1. 【強(qiáng)制】線程資源必須通過(guò)線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。
    說(shuō)明:使用線程池的好處是減少在創(chuàng)建和銷毀線程上所花的時(shí)間以及系統(tǒng)資源的開(kāi)銷,解決資
    源不足的問(wèn)題身冬。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或者
    “過(guò)度切換”的問(wèn)題衅胀。
  2. 【強(qiáng)制】線程池不允許使用 Executors 去創(chuàng)建,而是通過(guò) ThreadPoolExecutor 的方式,這樣的處理方式讓寫(xiě)的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
    --引自《阿里巴巴JAVA開(kāi)發(fā)手冊(cè)(紀(jì)念版)》編程規(guī)約第六條并發(fā)處理酥筝,第3和第4點(diǎn)

線程池的作用:

線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量滚躯。根據(jù)系統(tǒng)的環(huán)境情況,可以自動(dòng)或手動(dòng)設(shè)置線程數(shù)量嘿歌,達(dá)到運(yùn)行的最佳效果掸掏;少了浪費(fèi)了系統(tǒng)資源,多了造成系統(tǒng)擁擠效率不高宙帝。用線程池控制線程數(shù)量丧凤,其他線程排隊(duì)等候。一個(gè)任務(wù)執(zhí)行完畢步脓,再?gòu)年?duì)列的中取最前面的任務(wù)開(kāi)始執(zhí)行愿待。若隊(duì)列中沒(méi)有等待進(jìn)程,線程池的這一資源處于等待靴患。當(dāng)一個(gè)新任務(wù)需要運(yùn)行時(shí)仍侥,如果線程池中有等待的工作線程,就可以開(kāi)始運(yùn)行了鸳君;否則進(jìn)入等待隊(duì)列访圃。

使用線程池的原因:

  1. 減少了創(chuàng)建和銷毀線程的次數(shù),每個(gè)工作線程都可以被重復(fù)利用相嵌,可執(zhí)行多個(gè)任務(wù)腿时。
  2. 可以根據(jù)系統(tǒng)的承受能力况脆,調(diào)整線程池中工作線線程的數(shù)目,防止因?yàn)橄倪^(guò)多的內(nèi)存批糟,而把服務(wù)器累趴下(每個(gè)線程需要大約1MB內(nèi)存格了,線程開(kāi)的越多,消耗的內(nèi)存也就越大徽鼎,最后死機(jī))盛末。

Java里面線程池的頂級(jí)接口是Executor,但是嚴(yán)格意義上講Executor并不是一個(gè)線程池否淤,而只是一個(gè)執(zhí)行線程的工具悄但。真正的線程池接口是ExecutorService。
比較重要的幾個(gè)類:

接口 作用
ExecutorService 真正的線程池接口
ScheduledExecutorService 能和Timer/TimerTask類似石抡,解決那些需要任務(wù)重復(fù)執(zhí)行的問(wèn)題檐嚣。
ThreadPoolExecutor ExecutorService的默認(rèn)實(shí)現(xiàn)。
ScheduledThreadPoolExecutor 繼承ThreadPoolExecutor的ScheduledExecutorService接口實(shí)現(xiàn)啰扛,周期性任務(wù)調(diào)度的類實(shí)現(xiàn)嚎京。

???要配置一個(gè)線程池是比較復(fù)雜的,尤其是對(duì)于線程池的原理不是很清楚的情況下隐解,很有可能配置的線程池不是較優(yōu)的鞍帝,因此在Executors類里面提供了一些靜態(tài)工廠,生成一些常用的線程池煞茫。

  1. newSingleThreadExecutor
創(chuàng)建一個(gè)單線程的線程池帕涌。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)续徽。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束宵膨,那么會(huì)有一個(gè)新的線程來(lái)替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行炸宵。
  1. newFixedThreadPool
創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程谷扣,直到線程達(dá)到線程池的最大大小土全。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束会涎,那么線程池會(huì)補(bǔ)充一個(gè)新線程裹匙。
  1. newCachedThreadPool
創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過(guò)了處理任務(wù)所需要的線程末秃,
那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程概页,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來(lái)處理任務(wù)练慕。此線程池不會(huì)對(duì)線程池大小做限制惰匙,線程池大小完全依賴于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小技掏。
  1. newScheduledThreadPool
創(chuàng)建一個(gè)大小無(wú)限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求项鬼。

ThreadPoolExecutor詳解

ThreadPoolExecutor的完整構(gòu)造方法的簽名是:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

corePoolSize  //池中所保存的線程數(shù)哑梳,包括空閑線程。
maximumPoolSize  //池中允許的最大線程數(shù)绘盟。
keepAliveTime  //當(dāng)線程數(shù)大于核心時(shí)鸠真,此為終止前多余的空閑線程等待新任務(wù)的最長(zhǎng)時(shí)間。
unit  //keepAliveTime參數(shù)的時(shí)間單位龄毡。
workQueue  //執(zhí)行前用于保持任務(wù)的隊(duì)列吠卷。此隊(duì)列僅保持由 execute方法提交的 Runnable任務(wù)。
threadFactory  //執(zhí)行程序創(chuàng)建新線程時(shí)使用的工廠沦零。
handler  //由于超出線程范圍和隊(duì)列容量而使執(zhí)行被阻塞時(shí)所使用的處理程序祭隔。

ThreadPoolExecutor是Executors類的底層實(shí)現(xiàn)。

???在JDK幫助文檔中蠢终,有如此一段話:“強(qiáng)烈建議程序員使用較為方便的Executors工廠方法Executors.newCachedThreadPool()(無(wú)界線程池序攘,可以進(jìn)行自動(dòng)線程回收)、Executors.newFixedThreadPool(int)(固定大小線程池)Executors.newSingleThreadExecutor()(單個(gè)后臺(tái)線程)
它們均為大多數(shù)使用場(chǎng)景預(yù)定義了設(shè)置寻拂〕痰欤”
下面介紹一下幾個(gè)類的源碼:
ExecutorService newFixedThreadPool (int nThreads):固定大小線程池。
可以看到祭钉,corePoolSize和maximumPoolSize的大小是一樣的(實(shí)際上瞄沙,后面會(huì)介紹,如果使用無(wú)界queue的話maximumPoolSize參數(shù)是沒(méi)有意義的)慌核,keepAliveTime和unit的設(shè)值表名什么距境?-就是該實(shí)現(xiàn)不想keep alive!最后的BlockingQueue選擇了LinkedBlockingQueue垮卓,該queue有一個(gè)特點(diǎn)垫桂,他是無(wú)界的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粟按,一起剝皮案震驚了整個(gè)濱河市诬滩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灭将,老刑警劉巖疼鸟,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異庙曙,居然都是意外死亡空镜,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吴攒,“玉大人张抄,你說(shuō)我怎么就攤上這事〔案” “怎么了欣鳖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)茴厉。 經(jīng)常有香客問(wèn)我泽台,道長(zhǎng),這世上最難降的妖魔是什么矾缓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任怀酷,我火速辦了婚禮,結(jié)果婚禮上嗜闻,老公的妹妹穿的比我還像新娘蜕依。我一直安慰自己,他們只是感情好琉雳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布样眠。 她就那樣靜靜地躺著,像睡著了一般翠肘。 火紅的嫁衣襯著肌膚如雪檐束。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天束倍,我揣著相機(jī)與錄音被丧,去河邊找鬼。 笑死绪妹,一個(gè)胖子當(dāng)著我的面吹牛甥桂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播邮旷,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼黄选,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了婶肩?” 一聲冷哼從身側(cè)響起办陷,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狡孔,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜂嗽,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苗膝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了植旧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辱揭。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡离唐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出问窃,到底是詐尸還是另有隱情亥鬓,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布域庇,位于F島的核電站嵌戈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏听皿。R本人自食惡果不足惜熟呛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尉姨。 院中可真熱鬧庵朝,春花似錦、人聲如沸又厉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)覆致。三九已至侄旬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間篷朵,已是汗流浹背勾怒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留声旺,地道東北人笔链。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像腮猖,于是被迫代替她去往敵國(guó)和親鉴扫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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

  • 1.繼承Thread類創(chuàng)建線程 線程類 測(cè)試代碼 2.實(shí)現(xiàn)Runable接口創(chuàng)建線程 線程類 測(cè)試代碼 3.使用線...
    zxk175閱讀 184評(píng)論 0 0
  • 導(dǎo)讀目錄 線程組(ThreadGroup) 線程池(Thread Pool) Fork/Join框架和Execut...
    ql2012jz閱讀 1,458評(píng)論 0 0
  • 《增廣賢文》全文解釋 古訓(xùn)增廣澈缺,是中國(guó)幾千年人民生活經(jīng)驗(yàn)的結(jié)晶坪创,雖來(lái)自民間,卻有很多寶貴的東西姐赡,影響著一代又一代人...
    林曦雪閱讀 468評(píng)論 0 0
  • 左岸南楓閱讀 203評(píng)論 0 0
  • * 我發(fā)現(xiàn)早年的電影主創(chuàng)名單會(huì)放在前面莱预,我認(rèn)為如果都是大腕,名單放在前面會(huì)引起期待项滑,挺好的依沮,但如果是知名度不高的演...
    偊徉閱讀 969評(píng)論 0 0