java并发编程基础系列(五): 创建线程的四种方式
線程的創(chuàng)建一共有四種方式:
- 繼承于Thread類,重寫run()方法;
- 實現(xiàn)Runable接口,實現(xiàn)里面的run()方法;
- 使用 FutureTask 實現(xiàn)有返回結(jié)果的線程
- 使用ExecutorService、Executors 線程池。
在詳細了解這四種方法之前,先來理解一下為什么線程要這樣創(chuàng)建:形象點來說,Thread是一個工人,run()方法里面的便是他的任務(wù)欄,這個任務(wù)欄默認是空的。當(dāng)你想要這個線程做點什么時,你可以重寫Thread里面的run方法,重寫這個工人的任務(wù)欄;也可以通過runable、callable接口,從外部賦予這個工人任務(wù)。還可以將任務(wù)交給一堆工人,誰有空就誰就承擔(dān)這個任務(wù)(線程池)。
一、四種方式的詳細介紹
1、繼承于Thread類,重寫run()方法
Thread thread = new MyThread();//線程啟動 thread.start();MyThread 類
//繼承Thread class MyThread extends Thread{//重寫run方法@Overridepublic void run() {//任務(wù)內(nèi)容....System.out.println("當(dāng)前線程是:"+Thread.currentThread().getName());} }運行結(jié)果:
當(dāng)前線程是:Thread-0
如果線程類使用的很少,那么可以使用匿名內(nèi)部類,請看下面的例子:
Thread thread = new Thread(){@Overridepublic void run() {//任務(wù)內(nèi)容....System.out.println("當(dāng)前線程是:"+Thread.currentThread().getName());}};2、實現(xiàn)Runable接口,實現(xiàn)里面的run()方法:
第一種方法- -繼承Thread類的方法,一般情況下是不建議用的,因為java是單繼承結(jié)構(gòu),一旦繼承了Thread類,就無法繼承其他類了。所以建議使用 實現(xiàn)Runable接口 的方法;
Thread thread = new Thread(new MyTask());//線程啟動 thread.start();MyTask 類:
//實現(xiàn)Runnable接口 class MyTask implements Runnable{//重寫run方法public void run() {//任務(wù)內(nèi)容....System.out.println("當(dāng)前線程是:"+Thread.currentThread().getName());} }同樣,如果這個任務(wù)類(MyTask )用的很少,也可以使用匿名內(nèi)部類:
Thread thread = new Thread(new Runnable() {@Overridepublic void run() {//任務(wù)內(nèi)容....System.out.println("當(dāng)前線程是:"+Thread.currentThread().getName());}});3、使用 FutureTask 實現(xiàn)有返回結(jié)果的線程
FutureTask 是一個可取消的異步計算任務(wù),是一個獨立的類,實現(xiàn)了 Future、Runnable接口。FutureTask 的出現(xiàn)是為了彌補 Thread 的不足而設(shè)計的,可以讓程序員跟蹤、獲取任務(wù)的執(zhí)行情況、計算結(jié)果 。
??因為 FutureTask實現(xiàn)了 Runnable,所以 FutureTask 可以作為參數(shù)來創(chuàng)建一個新的線程來執(zhí)行,也可以提交給 Executor 執(zhí)行。FutureTask 一旦計算完成,就不能再重新開始或取消計算。
FutureTask的構(gòu)造方法
可以接受 Runnable,Callable 的子類實例。
//創(chuàng)建一個 FutureTask,一旦運行就執(zhí)行給定的 Callable。 public FutureTask(Callable<V> callable);//創(chuàng)建一個 FutureTask,一旦運行就執(zhí)行給定的 Runnable,并安排成功完成時 get 返回給定的結(jié)果 。 public FutureTask(Runnable runnable, V result)FutureTask 的簡單例子
public class Test {public static void main(String[] args) throws InterruptedException, ExecutionException {FutureTask<Double> task = new FutureTask(new MyCallable());//創(chuàng)建一個線程,異步計算結(jié)果Thread thread = new Thread(task);thread.start();//主線程繼續(xù)工作Thread.sleep(1000);System.out.println("主線程等待計算結(jié)果...");//當(dāng)需要用到異步計算的結(jié)果時,阻塞獲取這個結(jié)果Double d = task.get();System.out.println("計算結(jié)果是:"+d);//用同一個 FutureTask 再起一個線程Thread thread2 = new Thread(task);thread2.start(); } }class MyCallable implements Callable<Double>{@Overridepublic Double call() {double d = 0;try {System.out.println("異步計算開始.......");d = Math.random()*10;d += 1000;Thread.sleep(2000);System.out.println("異步計算結(jié)束.......");} catch (InterruptedException e) {e.printStackTrace();}return d;} }運行結(jié)果:
異步計算開始…
主線程等待計算結(jié)果…
異步計算結(jié)束…
計算結(jié)果是:1002.7806590582911
四、使用線程池ExecutorSerice、Executors
前面三種方法,都是顯式地創(chuàng)建一個線程,可以直接控制線程,如線程的優(yōu)先級、線程是否是守護線程,線程何時啟動等等。而第四種方法,則是創(chuàng)建一個線程池,池中可以有1個或多個線程,這些線程都是線程池去維護,控制程序員不需要關(guān)心這些細節(jié),只需要將任務(wù)提交給線程池去處理便可,非常方便。
??創(chuàng)建線程池的前提最好是你的任務(wù)量大,因為創(chuàng)建線程池的開銷比創(chuàng)建一個線程大得多。
創(chuàng)建線程池的方式
ExecutorService 是一個比較重要的接口,實現(xiàn)這個接口的子類有兩個 ThreadPoolExecutor (普通線程池)、ScheduleThreadPoolExecutor (定時任務(wù)的線程池)。你可以通過這兩個類來創(chuàng)建一個線程池,但要傳入各種參數(shù),不太方便。
??為了方便用戶,JDK中提供了工具類Executors,提供了幾個創(chuàng)建常用的線程池的工廠方法。由于篇幅原因,不細說,可參考我的并發(fā)系列文章。
Executors 創(chuàng)建單線程的線程池
public class MyTest {public static void main(String[] args) {//創(chuàng)建一個只有一個線程的線程池ExecutorService executorService = Executors.newSingleThreadExecutor();//創(chuàng)建任務(wù),并提交任務(wù)到線程池中executorService.execute(new MyRunable("任務(wù)1"));executorService.execute(new MyRunable("任務(wù)2"));executorService.execute(new MyRunable("任務(wù)3"));} }class MyRunable implements Runnable{private String taskName;public MyRunable(String taskName) {this.taskName = taskName;}@Overridepublic void run() {System.out.println("線程池完成任務(wù):"+taskName);} }二、關(guān)于run()方法的思考
看看下面這種情況:線程類Thread 接收了外部任務(wù),同時又用匿名內(nèi)部類的方式重寫了內(nèi)部的run()方法,這樣豈不是有兩個任務(wù),那么究竟會執(zhí)行那個任務(wù)呢?還是兩個任務(wù)一起執(zhí)行呢?
Thread thread = new Thread(new MyTask()){@Overridepublic void run() {//重寫Thread類的run方法System.out.println("Thread 類的run方法");}};//線程啟動thread.start(); //實現(xiàn)Runnable接口 class MyTask implements Runnable{//重寫run方法@Overridepublic void run() {//任務(wù)內(nèi)容....System.out.println("這是Runnable的run方法");} }運行結(jié)果:
Thread 類的run方法
通過上面的結(jié)果,可以看出:線程最后執(zhí)行的是Thread類內(nèi)部的run()方法,這是為什么呢?我們先來分析一下JDK的Thread源碼:
private Runnable target;public void run() { if (target != null) { target.run(); } }一切都清晰明了了,Thread類的run方法在沒有重寫的情況下,是判斷一下是否有Runnable 對象傳進來,如果有,那么就調(diào)用Runnable 對象里的run方法;否則,就什么都不干,線程結(jié)束。所以,針對上面的例子,一旦你繼承重寫了Thread類的run()方法,而你又想可以接收Runable類的對象,那么就要加上super.run(),執(zhí)行沒有重寫時的run方法,改造的例子如下:
Thread thread = new Thread(new MyTask()){@Overridepublic void run() {//重寫Thread類的run方法//調(diào)用父類Thread的run方法,即沒有重寫時的run方法super.run();System.out.println("Thread 類的run方法");}};運行結(jié)果:
這是Runnable的run方法
Thread 類的run方法
- 出處:http://www.cnblogs.com/jinggod/p/8485106.html
總結(jié)
以上是生活随笔為你收集整理的java并发编程基础系列(五): 创建线程的四种方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 并发基础篇(四): java中线程的状态
- 下一篇: Java 集合系列10: HashMap