Java多線程五種實現方式

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接口好,原因實現了接口還可以繼續繼承,繼承了類不能再繼承。

……
幫助他人,快樂自己,最後,感謝您的閱讀!
所以如有紕漏或者建議,還請讀者朋友們在評論區不吝指出!
……
個人網站…知識是一種寶貴的資源和財富,益發掘,更益分享…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章