Java多線程五種實現方式
- 繼承Thread類
- 實現Runnable接口
- 匿名內部類
- 實現Callable接口通過FutureTask包裝器來創建Thread線程
- 通過線程池創建線程,使用線程池接口ExecutorService結合Callable、Future實現有返回結果的多線程。
前面三種【無返回值】原因:通過重寫run方法,run方法的返回值是void,所以沒有辦法返回結果。在run方法處理異常時,不能拋出異常,只能用try{ }catch(){}中捕獲異常。
後面兩種【有返回值】原因:通過Callable接口,就要實現call方法,這個方法的返回值是Object,所以返回的結果可以放在Object對象中。
1. 繼承Thread方法
Thread類本質上是實現了Runnable接口的一個實例。通過自己的類直接 extends Thread,並重寫run()方法,就可以啓動新線程並執行自己定義的run()方法。
// Thread測試用例
public class MyThread extends Thread{
//重寫run方法
public void run() {
System.out.println("MyThread run()");
}
public static void main(String[] args) {
MyThread myThread = new MyThread ();
System.out.println("myThread.start()");
//啓動線程的唯一方法就是通過Thread類的start()實例方法
myThread.start();
}
}
運行結果:
myThread.start()
MyThread run()
JDK1.7 Thread類的start()方法源碼,start0()方法是一個native方法,它將啓動一個新線程,並執行run()方法。
public synchronized void start()
{
boolean flag;
if(threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
flag = false;
start0();
flag = true;
try
{
if(!flag)
group.threadStartFailed(this);
}
catch(Throwable throwable) { }
break MISSING_BLOCK_LABEL_70;
Exception exception;
exception;
try
{
if(!flag)
group.threadStartFailed(this);
}
catch(Throwable throwable1) { }
throw exception;
}
private native void start0();
2. 實現Runnable接口
如果自己的類已經extends另一個類,就無法直接extends Thread【Java只支持單繼承,不支持多繼承。一個類只能有一個父類,不可以有多個父類。】,此時,可以實現一個Runnable接口
public class ThreadDemo {
public static void main(String[] args){
//實現類的實例作爲Thread的target作爲參數傳入帶參的Thread構造函數
Thread thread = new Thread(new MyThread());
System.out.println("thread.start()");
//調用start()方法啓動線程
thread.start();
}
}
class MyThread extends Object implements Runnable{
@Override
public void run() {
System.out.println("MyThread run()");
}
}
運行結果:
thread.start()
MyThread run()
3. 使用匿名內部類
public class ThreadDemo {
public static void main(String[] args){
System.out.println("thread.start()");
//通過創建匿名內部類Runnable作爲參數對象創建一個線程
Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("MyThread run()");
}
});
//調用start()方法啓動線程
thread.start();
}
}
運行結果:
thread.start()
MyThread run()
4. 實現Callable接口通過FutureTask包裝器來創建Thread線程
1). 創建Callable接口的實現類 ,並重寫Call()方法
2). 使用Callable創建一個FutureTask對象,該FutureTask對象封裝了Callable對象的Call方法的返回值
3). 使用futureTask對象作爲Thread對象的target創建並啓動線
4) .調用FutureTask對象的get()方法來獲取返回值. 注意:get方法是阻塞的,即:線程無返回結果,get方法會一直等待。
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Object> callable = new MyCallable();
//使用Callable<Integer>創建一個FutureTask<Integer>對象
FutureTask<Object> futureTask= new FutureTask<Object>(callable);
//使用futureTask對象作爲Thread對象的target創建並啓動線程
Thread thread = new Thread(futureTask);
System.out.println("thread start!");
thread.start();
System.out.println(futureTask.get().toString());
System.out.println("thread end!");
}
}
/**創建Callable接口的實現類 ,並實現Call方法*/
class MyCallable implements Callable<Object>{
//重寫call方法
@Override
public Object call() throws Exception {
System.out.println("Tickets call...執行開始!");
return "Tickets call...執行完成!";
}
}
運行結果:
thread start!
Tickets call...執行開始!
Tickets call...執行完成!
thread end!
5. 通過線程池創建線程,使用線程池接口ExecutorService結合Callable、Future實現有返回結果的多線程。
ExecutorService、Callable、Future三個接口實際上都是屬於Executor框架。需要有返回值的任務必須實現Callable接口。執行Callable任務後,可以獲取一個Future的對象,Future對象的get方法就可以獲取到Callable任務返回的Object了。
注意:get方法是阻塞的,即:線程無返回結果,get方法會一直等待。
public class ThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("通過ExecutorService線程池 創建有返回值的任務開始……");
//創建一個線程池
ExecutorService pool = Executors.newCachedThreadPool();
Callable callable = new MyCallable();
//執行任務並獲取Future對象
Future f = pool.submit(callable);
System.out.println(f.get().toString());
// 關閉線程池
pool.shutdown();
System.out.println("通過ExecutorService線程池 創建有返回值的任務結束!");
}
}
class MyCallable implements Callable<Object> {
@Override
public Object call() throws Exception {
System.out.println(">>>MyCallable call()任務啓動……");
return ">>>MyCallable call()任務結束!";
}
}
運行結果:
通過ExecutorService線程池 創建有返回值的任務開始……
>>>MyCallable call()任務啓動……
>>>MyCallable call()任務結束!
通過ExecutorService線程池 創建有返回值的任務結束!
擴展…1【Executors類】
Executors類:提供了一系列工廠方法用於創建線程池,返回的線程池都實現了ExecutorService接口。
ExecutorService接口:提供了submit()方法,傳遞一個Callable或Runnable,返回Future。如果Executor後臺線程池還沒有完成Callable的計算,這調用返回Future對象的get()方法,會阻塞直到計算完成。
//創建一個可緩存的線程池,調用execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。
public static ExecutorService newCachedThreadPool()
//創建固定數目線程的線程池。
public static ExecutorService newFixedThreadPool(int nThreads)
//創建一個單線程化的Executor。
public static ExecutorService newSingleThreadExecutor()
//創建一個支持 [定時及週期性] 的任務執行的線程池,多數情況下可用來替代Timer類。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
擴展…2【實際的java開發中是繼承Thread類好,還是實現Runnable接口好?】
因爲java是單繼承,所以使用實現實現Runnable接口好,原因實現了接口還可以繼續繼承,繼承了類不能再繼承。
……
幫助他人,快樂自己,最後,感謝您的閱讀!
所以如有紕漏或者建議,還請讀者朋友們在評論區不吝指出!
……
個人網站…知識是一種寶貴的資源和財富,益發掘,更益分享…