模擬可取消任務的股票交易處理程式(百萬訂單)(FutureTask類)

Guo_1_9發表於2018-02-15

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%,取決你的電腦配置.

相關文章