Java多線程編程
Callable
Runnable封裝了一個異步運行的任務,可以把它想象成為一個沒有參數和返回值的異步方法。Callable和Runnable類似,但是有返回值,Callable接口是一個參數化的類型,只有一個方法call
public interface Callable<V>
{
V call() throws Exception
}
Future
Future可以保存異步計算的結果,可以啟動一個計算,將Future對象交給某個線程,Future對象的所有者在結果計算好之后就可以獲得它
Future接口具有下面的方法:
public interface Future<V>
{
V get() throws...;
V get(long timeout, TimeUnit unit) throws...;
void cancel(boolean mayInterrupt);
boolean isCancelled();
boolean isDone();
}
FutureTask包裝器
可以將Callable轉換成Future和Runnable,它實現了二者的接口
Callable<Integer> myComputation = ...;
FutureTask<Integer> task = new FutureTask<Integer>(myComputation);
Thread thread = new Thread(task);
thread.start();
...
Integer result = task.get(); // It `s future
調用get()方法會發生阻塞,直到結果可以獲得為止。
相關事例
在Java多線程處理任務過程中,由于每一個線程處理完自身任務之后,需要將數據傳出來,然后再經過整合,完成這個業務功能。
鑒于線程自身運行完畢階段無法返回數據,可以通過Future對象交給線程,之后通過Future對象獲取到計算結果
通過FutureTask包裝器將Callable轉換成Future和Runnable;
比如
package future;
import java.util.concurrent.*;
public class FutureTaskCount {
// 定義4個計算線程
Callable<Integer> calculate1 = new Callable<Integer>() {
private int count = 0;
@Override
public Integer call() throws Exception {
for (int i = 0; i < 4; i++) {
count++;
System.out.println("此時線程1正在運行 當前count: " + count);
Thread.sleep(1000);
}
return count;
}
};
Callable<Integer> calculate2 = new Callable<Integer>() {
private int count = 0;
@Override
public Integer call() throws Exception {
for (int i = 0; i < 4; i++) {
count++;
System.out.println("此時線程2正在運行 當前count: " + count);
Thread.sleep(1000);
}
return count;
}
};
Callable<Integer> calculate3 = new Callable<Integer>() {
private int count = 0;
@Override
public Integer call() throws Exception {
for (int i = 0; i < 4; i++) {
count++;
System.out.println("此時線程3正在運行 當前count: " + count);
Thread.sleep(1000);
}
return count;
}
};
Callable<Integer> calculate4 = new Callable<Integer>() {
private int count = 0;
@Override
public Integer call() throws Exception {
for (int i = 0; i < 4; i++) {
count++;
System.out.println("此時線程4正在運行 當前count: " + count);
Thread.sleep(1000);
}
return count;
}
};
public static void main(String[] args) {
FutureTaskCount futureTaskCount = new FutureTaskCount();
// 獲取線程池
ExecutorService executorService = Executors.newCachedThreadPool();
FutureTask calculate1 = new FutureTask<Integer>(futureTaskCount.calculate1);
FutureTask calculate2 = new FutureTask<Integer>(futureTaskCount.calculate2);
FutureTask calculate3 = new FutureTask<Integer>(futureTaskCount.calculate3);
FutureTask calculate4 = new FutureTask<Integer>(futureTaskCount.calculate4);
executorService.submit(calculate1);
executorService.submit(calculate2);
executorService.submit(calculate3);
executorService.submit(calculate4);
try {
System.out.println(calculate1.get());
System.out.println(calculate2.get());
System.out.println(calculate3.get());
System.out.println(calculate4.get());
System.out.println("所有線程計算均執行完畢");
} catch (InterruptedException exception) {
exception.printStackTrace();
} catch (ExecutionException exception) {
exception.printStackTrace();
}
}
}
當所有線程的計算均完畢時候,才會打印出 "所有線程計算均執行完畢",也就是調用get()方法會發生阻塞,直到結果可以獲得為止。executorService.submit
的調用,相當于啟動線程操作了,以上代碼中需要將call()方法overwrite。此時,每個線程可以獨立運行任務。最后直接調用get()
獲取每個線程計算結果
執行器(Executor)
執行器有許多方法來構建線程池,比如newCachedThread
方法構建了一個線程池,對于每個任務,如果有空閑線程可用,立即讓它執行任務,如果沒有可用的線程,則創建一個新線程,那么把得不到服務的任務放置到隊列中。當其他任務完成以后再運行它們。