FutureTask類
重點是那個股票交易處理程式的例子,認真看三遍。本文花了三個小時。
GitHub程式碼歡迎star。
小白認為學習語言最好的方式就是模仿、思考別人為什麼這麼寫。
FutureTask類同時實現類Runnable介面和Future介面。因此,FutureTask類技能擁有Runnable介面提供的非同步計算能力,也能擁有Future介面提供的返回值給呼叫方的Future物件取消任務的能力。FutureTask類可以用於封裝Callable和Runnable介面。
//Future<Integer> future = executor.submit(Callable);
FutureTask<Integer> future = new FutureTaks<Integer>(Callable);
future.run()
複製程式碼
run方法會呼叫任務,並將任務的計算結果賦值給Future物件。
也可以將FutureTask例項交給Executor物件用於執行。
executor.submit(future);
複製程式碼
由於FutureTask類也實現了Future介面,因此FutureTak介面例項可以用來取消任務,檢查任務等。
建立可取消的任務。
取消任務可以使用執行器返回的Future物件,而建立和執行任務可以使用前面討論的FutureTask類。
開發可以處理上百萬次請求的模擬器。會傳送數千條資料交易請求給模擬器。模擬器包含的執行緒池用於處理這些請求。 還將編寫一個“邪惡”的執行緒,它會隨機選擇諾幹訂單,並且嘗試取消他們。如果訂單已經執行,取消請求會失敗。 如果在訂單在被分配給執行緒執行之前接收到取消請求,那麼訂單會被取消。如果交易訂單正在執行。並且執行緒可被中斷, 那麼在訂單處理過程中接收的取消請求會結束剩餘的處理流程。從而取消訂單。
/**
* Created by guo on 2018/2/15.
* 演示可取消任務的股票交易處理程式
*/
public class StocksOrderProcessor {
static final int MAX_NUMBER_OF_ORDER = 1_000_000; //交易訂單
//1、建立數量為1000的執行緒池來執行訂單。經過測試1000個執行緒,CPU維持在70%-80%左右。
static private ExecutorService executor = Executors.newFixedThreadPool(1000);
//2、建立ArrayList來儲存執行執行訂單的引用
static private List<Future> ordersToProcess = new ArrayList<>();
/**
* 建立內部私有類OrderExecutor以處理訂單執行的業務邏輯。
* OrderExecutor實現了Callable介面以支援非同步呼叫。
*/
public static class OrderExecutor implements Callable {
int id = 0;
int count = 0;
//3、傳入整型變數id來記錄訂單編號。
public OrderExecutor(int id) {
this.id = id;
}
@Override
public Object call() throws Exception {
try {
//4、將技術設為1000,每次計數前,讓執行緒休眠一段不同的時間
while (count < 1000) {
count++;
//5、通過讓執行緒休眠一段不同的時間,模擬現實中每個訂單需要不同的處理時間。
Thread.sleep(new Random(
System.currentTimeMillis() % 10).nextInt(10));
}
System.out.println("Successfully executed order:" + id);
} catch (Exception ex) {
throw (ex);
}
return id;
}
}
}
複製程式碼
主函式
public static void main(String[] args) {
System.out.printf("Submitting %d trades%n", MAX_NUMBER_OF_ORDER);
//6、通過迴圈遍歷,提交一百萬訂單。
for (int i = 0; i < MAX_NUMBER_OF_ORDER; i++) {
submitOrder(i);
}
//7、建立“邪惡”執行緒嘗試隨機的取消某些訂單。
//每當執行到這裡時,就會建立一些取消請求,並針對待處理的訂單列表中儲存的Future物件執行。
new Thread(new EvilThread(ordersToProcess)).start();
System.out.println("Cancelling a few order at random");
try {
//8a、某些訂單可能已經被處理,模擬器就會繼續處理剩餘訂單。
// b、如果訂單在執行器分配執行緒之前被取消,就將永遠不會執行。
// c、為了留有足夠的時間結束所有待處理的訂單,讓執行器等待30秒。
executor.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Checking status before shutdown");
int count = 0;
//9a、通過迴圈遍歷,統計有多少訂單被成功取消。
// b、對於訂單中每一個Future物件,呼叫isCancelld方法。
// c、如果對應的被成功取消,則方法返回true
for (Future f : ordersToProcess) {
if (f.isCancelled()) {
count++;
}
}
System.out.printf("%d trades cancelled%n", count);
//10、立即停止執行器釋放分配的所有資源 (貌似我的百萬訂單根本停不下來啊,求解!)
executor.shutdownNow();
}
private static void submitOrder(int id) {
//6、a 建立一個Callable例項,每個例項都有為一個的Id供跟蹤
Callable<Integer> callable = new OrderExecutor(id);
//6、b 呼叫ExecutorService的submit方法可將建立的任務提交以待執行。
//並且將submit方法返回的物件放到待處理訂單的陣列裡列表中。
ordersToProcess.add(executor.submit(callable));
}
複製程式碼
邪惡執行緒
/**
* 邪惡執行緒,隨機的取消某些訂單。
*/
class EvilThread implements Runnable {
private List<Future> ordersToProcess;
//1、在建構函式中傳入待處理的訂單列表,這樣可以對某一些Future物件傳送取消請求。
public EvilThread(List<Future> future) {
this.ordersToProcess = future;
}
@Override
public void run() {
//2、建立100個取消請求
Random myNextKill = new Random(System.currentTimeMillis() % 100);
for (int i = 0; i < 100; i++) {
//3、隨機選擇Future物件進行取消。
int index = myNextKill.nextInt(StocksOrderProcessor.MAX_NUMBER_OF_ORDER);
//4、呼叫Future物件的cancel方法以傳送請求,並將cancel方法的引數設為ture。表示任務可能會在執行過程中被中斷。
boolean cancel = ordersToProcess.get(index).cancel(true);
//5、判斷是否取消成功,
if (cancel) {
System.out.println("Cancel Order Succeded:" + index);
} else {
System.out.println("cancel Order Failed:" + index);
}
try {
//6、在每兩個請求之間讓“邪惡”執行緒睡一會。
Thread.sleep(myNextKill.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
複製程式碼
程式執行後部分輸出如下:
Submitting 1000000 trades
Successfully executed order:28
Successfully executed order:380
Successfully executed order:288
Successfully executed order:120
Cancelling a few order at random
Successfully executed order:116
Successfully executed order:1004
Successfully executed order:1005
Cancel Order Succeded:698021
cancel Order Failed:98832(重點)
...
Successfully executed order:12268
Successfully executed order:12420
Successfully executed order:13190
Successfully executed order:12199
Checking status before shutdown
99 trades cancelled(重點)
Successfully executed order:14045 //估計Kill執行緒太多了,遺漏這個了.求解.
複製程式碼
從輸出可以看到:
- 訂單698021被成功取消,這個訂單還未執行,
- 訂單98832的取消請求失敗了,因為這個訂單已經執行結束.
- 在傳送的100個請請求中,有99個被成功取下.也可能是100%,取決你的電腦配置.