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的聯繫與區別見下一篇博文。