Java創建並執行線程的四種方法

java裏面創建線程有四種方式:
無返回:
1. 實現Runnable接口,重寫run();
2. 繼承Thread類,重寫run();
有返回:
1. 實現Callable接口,重寫call(),利用FutureTask包裝Callable,並作爲task傳入Thread構造函數;
2. 利用線程池;

下面來看看具體的創建方式:
1. 繼承Thread類,重寫run();
創建:創建線程只需要繼承Thread類,然後在run方法裏寫下線程要實現的任務即可;
這裏寫圖片描述
調用:通過調用start方法來啓動線程而不能直接調用run方法。
這裏寫圖片描述
① Thread類本質上是實現了Runnable接口,Thread對象代表一個線程的實例。
② Runnable接口只有一個抽象的run()方法。
③ 啓動線程的唯一方法就是通過Thread類的start()實例方法。
④ start()方法是一個native方法,它將啓動一個新線程,並執行run()方法。
⑤ 自定義類直接extend Thread,並複寫run()方法,就可以啓動新線程並執行自己定義的run()方法。
2. 創建任務,實現Runnable接口,重寫run()。(受歡迎)
因爲Java只能單繼承,繼承了Thread類就不能再繼承別的類了所以實現繼承更推崇的是讓線程類實現Runnable接口。
這裏寫圖片描述
將Runnbale作爲參數傳入Thread的構造函數,創建Thread.
這裏寫圖片描述
3. Callable接口只包含抽象方法V call()。
這裏寫圖片描述
利用Callable接口創建並啓動新線程的步驟:
① 定義MyClass實現Callable接口;Class MyClass implements Callable
② 重寫call(),將執行的代碼寫入;
③ 創建FutureTask的對象;FutureTask中定義了run(),run()內部調用了call(),並保存了call()的返回值;FutureTask futuretask = new FutureTask(newMyClass());
④ 創建Thread的對象;Thread thread = new Thread(futuretask);//傳入參數Runnable接口
⑤ 啓動線程;thread.start();[圖片]
⑥ 可通過FutureTask類的get()方法獲得線程執行結束後的返回值,即call的返回值。futuretask.get();

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class MyThread {

    public static void main(String[] args) throws InterruptedException {
        FutureTask<Integer> task = new FutureTask<Integer>(new CallableImpl());
        Thread thread = new Thread(task);
        thread.start();
        try {
            System.out.println("task.get() returns " + task.get());
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class CallableImpl implements Callable<Integer> {

    private static Integer value = 0;

    @Override
    public Integer call() throws Exception {
        System.out.println("執行call方法之前 value = " + value);
        value = value.intValue() + 1;
        System.out.println("執行call方法之後 value = " + value);
        return value;
    }
}

運行結果:
執行call方法之前 value = 0
執行call方法之後 value = 1
task.get() returns 1
4. 通過線程池來創建線程
① new ThreadPoolExecutor(…);
② 創建任務Task implements Callable,重寫run()方法;
③ 通過線程池的execute()或submit()將任務command傳入線程池;
④ 獲取返回值:
1) 實現Callable接口,重寫call()方法
class CallableImpl implements Callable
2) 定義線程池
ThreadPoolExecutor executor
3) 利用submit()方法提交任務
Future<?> future = executor.submit(new CallableImpl());
5) 利用FutureTask類get()方法獲取返回值
res = task.get();
這裏future申明爲Future對象,但是它是由FutureTask實現的,也可以直接申明爲FutureTask future:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPool {
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(20));
        Future task;
        for (int i = 0; i < 5; i++) {
            task = executor.submit(new CallableImpl());
            System.out.println("線程返回結果:" + task.get());
        }
        executor.shutdown();
    }
}

class RunnableImpl implements Runnable {

    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println("hhh");
        System.out.println(Thread.currentThread().getName());
    }
}

運行結果:
執行call方法之前 value = 0
執行call方法之後 value = 1
線程返回結果:1
執行call方法之前 value = 1
執行call方法之後 value = 2
線程返回結果:2
執行call方法之前 value = 2
執行call方法之後 value = 3
線程返回結果:3
執行call方法之前 value = 3
執行call方法之後 value = 4
線程返回結果:4
執行call方法之前 value = 4
執行call方法之後 value = 5
線程返回結果:5

總結:
線程的創建有四種方式:主要分爲有返回和無返回,具體根據使用場景來選擇。
1. 如果不需要返回且線程數量小,則建議採用實現Runnable接口,重寫run()的方式;
2. 如果需要返回且線程數量小,則建議採用實現Callable接口,重寫call(),利用FutureTask包裝成一個Runnable,再作爲參數傳入Thread的構造方法的方式創建線程;
3. 如果線程數量較多,則建議採用線程池方式創建:execute提交任務實現無返回操作,submit提交任務實現有返回操作。

補充:FutureTask
JDK1.8中FutureTask實現了RunnableFuture,而RunnableFuture顧名思義就是Runnable接口和Future接口的結合體。因此,FutureTask對象可以作爲Runnable對象來用,比如

    Thread thread = new Thread(new FutureTask<V>());

也可以作爲Future來用。這就是Java的有魅力的地方。。。

    public class FutureTask<V> implements RunnableFuture<V>
    public interface RunnableFuture<V> extends Runnable, Future<V> {
        void run();
    }

Callable、Future、Runnable、FutureTask的聯繫與區別見下一篇博文。

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