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