首先,定義Thread類的子類並重寫run()方法:html
package com.zwwhnly.springbootaction.javabase.thread; public class MyFirstThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.printf("[MyFirstThread]輸出:%d,當前線程名稱:%s\n", i, getName()); } } }
而後,建立該子類的實例並調用start()方法啓動線程:java
package com.zwwhnly.springbootaction.javabase.thread; public class ThreadTest { public static void main(String[] args) { System.out.println("主線程開始執行,當前線程名稱:" + Thread.currentThread().getName()); Thread firstThread = new MyFirstThread(); firstThread.start(); System.out.println("主線程執行結束,當前線程名稱:" + Thread.currentThread().getName()); } }
運行結果以下所示:git
主線程開始執行,當前線程名稱:maingithub
主線程執行結束,當前線程名稱:main面試
[MyFirstThread]輸出:0,當前線程名稱:Thread-0spring
[MyFirstThread]輸出:1,當前線程名稱:Thread-0springboot
[MyFirstThread]輸出:2,當前線程名稱:Thread-0微信
[MyFirstThread]輸出:3,當前線程名稱:Thread-0多線程
[MyFirstThread]輸出:4,當前線程名稱:Thread-0併發
從運行結果能夠看出如下2個問題:
firstThread.start();
,run()方法體中的代碼並無當即執行,而是異步執行的。查看Thread類的源碼,能夠發現Thread類實現了接口Runnable:
public class Thread implements Runnable { // 省略其它代碼 }
這裏是重點,面試常問!
首先,定義Runnable接口的實現類並實現run()方法:
package com.zwwhnly.springbootaction.javabase.thread; public class MySecondThread implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.printf("[MySecondThread]輸出:%d,當前線程名稱:%s\n", i, Thread.currentThread().getName()); } } }
而後,調用Thread類的構造函數建立Thread實例並調用start()方法啓動線程:
package com.zwwhnly.springbootaction.javabase.thread; public class ThreadTest { public static void main(String[] args) { Runnable target = new MySecondThread(); Thread secondThread = new Thread(target); secondThread.start(); } }
運行結果以下所示:
主線程開始執行,當前線程名稱:main
主線程執行結束,當前線程名稱:main
[MySecondThread]輸出:0,當前線程名稱:Thread-0
[MySecondThread]輸出:1,當前線程名稱:Thread-0
[MySecondThread]輸出:2,當前線程名稱:Thread-0
[MySecondThread]輸出:3,當前線程名稱:Thread-0
[MySecondThread]輸出:4,當前線程名稱:Thread-0
能夠看出,使用這種方式和繼承Thread類的運行結果是同樣的。
首先,定義Callable接口的實現類並實現call()方法:
package com.zwwhnly.springbootaction.javabase.thread; import java.util.Random; import java.util.concurrent.Callable; public class MyThirdThread implements Callable<Integer> { @Override public Integer call() throws Exception { Thread.sleep(6 * 1000); return new Random().nextInt(); } }
而後,調用FutureTask類的構造函數建立FutureTask實例:
Callable<Integer> callable = new MyThirdThread(); FutureTask<Integer> futureTask = new FutureTask<>(callable);
最後,調用Thread類的構造函數建立Thread實例並調用start()方法啓動線程:
package com.zwwhnly.springbootaction.javabase.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class ThreadTest { public static void main(String[] args) { System.out.println("主線程開始執行,當前線程名稱:" + Thread.currentThread().getName()); Callable<Integer> callable = new MyThirdThread(); FutureTask<Integer> futureTask = new FutureTask<>(callable); new Thread(futureTask).start(); try { System.out.println("futureTask.isDone() return:" + futureTask.isDone()); System.out.println(futureTask.get()); System.out.println("futureTask.isDone() return:" + futureTask.isDone()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } System.out.println("主線程執行結束,當前線程名稱:" + Thread.currentThread().getName()); } }
運行結果以下所示:
主線程開始執行,當前線程名稱:main
futureTask.isDone() return:false
-1193053528
futureTask.isDone() return:true
主線程執行結束,當前線程名稱:main
能夠發現,使用Callable接口這種方式,咱們能夠經過futureTask.get()
獲取到線程的執行結果,而以前的2種方式,都是沒有返回值的。
注意事項:調用
futureTask.get()
獲取線程的執行結果時,主線程會阻塞直到獲取到結果。
阻塞效果以下圖所示:
如下是重點,面試常問!
關於第2點,能夠經過以下示例來理解。
假如咱們總共有10張票(共享的資源),爲了提高售票的效率,開了3個線程來售賣,代碼以下所示:
package com.zwwhnly.springbootaction.javabase.thread; public class SaleTicketThread implements Runnable { private int quantity = 10; @Override public void run() { while (quantity > 0) { System.out.println(quantity-- + " is saled by " + Thread.currentThread().getName()); } } }
public static void main(String[] args) { Runnable runnable = new SaleTicketThread(); Thread saleTicketThread1 = new Thread(runnable); Thread saleTicketThread2 = new Thread(runnable); Thread saleTicketThread3 = new Thread(runnable); saleTicketThread1.start(); saleTicketThread2.start(); saleTicketThread3.start(); }
由於3個線程都是異步執行的,所以每次的運行結果多是不同,如下列舉2次不一樣的運行結果。
第1次運行結果:
10 is saled by Thread-0
8 is saled by Thread-0
7 is saled by Thread-0
5 is saled by Thread-0
9 is saled by Thread-1
3 is saled by Thread-1
2 is saled by Thread-1
1 is saled by Thread-1
4 is saled by Thread-0
6 is saled by Thread-2
第2次運行結果:
10 is saled by Thread-0
9 is saled by Thread-0
8 is saled by Thread-0
7 is saled by Thread-0
6 is saled by Thread-0
5 is saled by Thread-0
3 is saled by Thread-0
2 is saled by Thread-0
4 is saled by Thread-2
1 is saled by Thread-1
若是將上面的SaleTicketThread修改爲繼承Thread類的方式,就變成了3個線程各自擁有10張票,即變成了30張票,而不是3個線程共享10張票。
由於實現Runnable接口的優點,基本上實現多線程都使用的是該種方式,因此咱們將以前定義的MyFirstThread也修改成實現Runnable接口的方式:
package com.zwwhnly.springbootaction.javabase.thread; public class MyFirstThread implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.printf("[MyFirstThread]輸出:%d,當前線程名稱:%s\n", i, Thread.currentThread().getName()); } } }
而後仍然沿用以前定義的MyFirstThread、MySecondThread,咱們先看下調用start()的效果:
package com.zwwhnly.springbootaction.javabase.thread; public class ThreadTest { public static void main(String[] args) { System.out.println("主線程開始執行,當前線程名稱:" + Thread.currentThread().getName()); Thread firstThread = new Thread(new MyFirstThread()); Runnable target = new MySecondThread(); Thread secondThread = new Thread(target); firstThread.start(); secondThread.start(); System.out.println("主線程執行結束,當前線程名稱:" + Thread.currentThread().getName()); } }
運行結果(注意:屢次運行,結果可能不同):
主線程開始執行,當前線程名稱:main
[MyFirstThread]輸出:0,當前線程名稱:Thread-0
[MyFirstThread]輸出:1,當前線程名稱:Thread-0
[MySecondThread]輸出:0,當前線程名稱:Thread-1
主線程執行結束,當前線程名稱:main
[MySecondThread]輸出:1,當前線程名稱:Thread-1
[MySecondThread]輸出:2,當前線程名稱:Thread-1
[MySecondThread]輸出:3,當前線程名稱:Thread-1
[MySecondThread]輸出:4,當前線程名稱:Thread-1
[MyFirstThread]輸出:2,當前線程名稱:Thread-0
[MyFirstThread]輸出:3,當前線程名稱:Thread-0
[MyFirstThread]輸出:4,當前線程名稱:Thread-0
能夠看出,調用start()方法後,程序中有3個線程,分別爲主線程main、Thread-0、Thread-1,並且執行順序不是按順序執行的,存在不肯定性。
而後將start()方法修改成run()方法,以下所示:
firstThread.run(); secondThread.run();
此時的運行結果以下所示(屢次運行,結果是同樣的):
主線程開始執行,當前線程名稱:main
[MyFirstThread]輸出:0,當前線程名稱:main
[MyFirstThread]輸出:1,當前線程名稱:main
[MyFirstThread]輸出:2,當前線程名稱:main
[MyFirstThread]輸出:3,當前線程名稱:main
[MyFirstThread]輸出:4,當前線程名稱:main
[MySecondThread]輸出:0,當前線程名稱:main
[MySecondThread]輸出:1,當前線程名稱:main
[MySecondThread]輸出:2,當前線程名稱:main
[MySecondThread]輸出:3,當前線程名稱:main
[MySecondThread]輸出:4,當前線程名稱:main
主線程執行結束,當前線程名稱:main
能夠看出,調用run()方法後,程序中只有一個主線程,自定義的2個線程並無啓動,並且執行順序也是按順序執行的。
如下是重點,面試常問!
在文章前面的章節中(1.2 實現Runnable接口 和1.3 實現Callable接口),咱們瞭解瞭如何使用Runnable、Callable接口來建立線程,如今咱們分別看下Runable和Callable接口的定義,其中,Runable接口的定義以下所示:
public interface Runnable { public abstract void run(); }
Callable接口的定義以下所示:
public interface Callable<V> { V call() throws Exception; }
由此能夠看出,Runnable和Callable的區別主要有如下幾點:
源碼地址:https://github.com/zwwhnly/springboot-action.git,歡迎下載。
Java Thread 的 run() 與 start() 的區別
若是以爲文章寫的不錯,歡迎關注個人微信公衆號:「申城異鄉人」,全部博客會同步更新。
若是有興趣,也能夠添加個人微信:zwwhnly_002,一塊兒交流和探討技術。