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

FutureTask類

重點是那個股票交易處理程序的例子,認真看三遍。本文花了三個小時。java

GitHub代碼歡迎star。git

小白認爲學習語言最好的方式就是模仿思考別人爲何這麼寫。github

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對象。dom

也能夠將FutureTask實例交給Executor對象用於執行。異步

executor.submit(future);

因爲FutureTask類也實現了Future接口,所以FutureTak接口實例能夠用來取消任務,檢查任務等。ide

建立可取消的任務。

取消任務可使用執行器返回的Future對象,而建立和執行任務可使用前面討論的FutureTask類。函數

開發能夠處理上百萬次請求的模擬器。會發送數千條數據交易請求給模擬器。模擬器包含的線程池用於處理這些請求。
還將編寫一個「邪惡」的線程,它會隨機選擇諾幹訂單,而且嘗試取消他們。若是訂單已經執行,取消請求會失敗。
若是在訂單在被分配給線程執行以前接收到取消請求,那麼訂單會被取消。若是交易訂單正在執行。而且線程可被中斷,
那麼在訂單處理過程當中接收的取消請求會結束剩餘的處理流程。從而取消訂單。學習

/**
 * Created by guo on 2018/2/15.
 * 演示可取消任務的股票交易處理程序
 */
public class StocksOrderProcessor {
    static final int MAX_NUMBER_OF_ORDER = 1_000_000;       //交易訂單
    //一、建立數量爲1000的線程池來執行訂單。通過測試1000個線程,CPU維持在70%-80%左右。
    static private ExecutorService executor = Executors.newFixedThreadPool(1000);
    //二、建立ArrayList來保存執行執行訂單的引用
    static private List<Future> ordersToProcess = new ArrayList<>();

    /**
     * 建立內部私有類OrderExecutor以處理訂單執行的業務邏輯。
     * OrderExecutor實現了Callable接口以支持異步調用。
     */
    public static class OrderExecutor implements Callable {
        int id = 0;
        int count = 0;
         //三、傳入整型變量id來記錄訂單編號。
        public OrderExecutor(int id) {
            this.id = id;
        }

        @Override
        public Object call() throws Exception {
            try {
                //四、將技術設爲1000,每次計數前,讓線程休眠一段不一樣的時間
                while (count < 1000) {
                    count++;
                    //五、經過讓線程休眠一段不一樣的時間,模擬現實中每一個訂單須要不一樣的處理時間。
                    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);
    //六、經過循環遍歷,提交一百萬訂單。
    for (int i = 0; i < MAX_NUMBER_OF_ORDER; i++) {
        submitOrder(i);
    }
    //七、建立「邪惡」線程嘗試隨機的取消某些訂單。
    //每當執行到這裏時,就會建立一些取消請求,並針對待處理的訂單列表中存儲的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);
    //十、當即中止執行器釋放分配的全部資源 (貌似個人百萬訂單根本停不下來啊,求解!)
    executor.shutdownNow();

}

private static void submitOrder(int id) {
    //六、a 建立一個Callable實例,每一個實例都有爲一個的Id供跟蹤
    Callable<Integer> callable = new OrderExecutor(id);
    //六、b 調用ExecutorService的submit方法可將建立的任務提交以待執行。
    //而且將submit方法返回的對象放到待處理訂單的數組裏列表中。
    ordersToProcess.add(executor.submit(callable));
}

邪惡線程


/**
 * 邪惡線程,隨機的取消某些訂單。
 */
class EvilThread implements Runnable {
    private List<Future> ordersToProcess;
     //一、在構造函數中傳入待處理的訂單列表,這樣能夠對某一些Future對象發送取消請求。
    public EvilThread(List<Future> future) {
        this.ordersToProcess = future;
    }

    @Override
    public void run() {
         //二、建立100個取消請求
        Random myNextKill = new Random(System.currentTimeMillis() % 100);
        for (int i = 0; i < 100; i++) {
            //三、隨機選擇Future對象進行取消。
            int index = myNextKill.nextInt(StocksOrderProcessor.MAX_NUMBER_OF_ORDER);
            //四、調用Future對象的cancel方法以發送請求,並將cancel方法的參數設爲ture。表示任務可能會在執行過程當中被中斷。
            boolean cancel = ordersToProcess.get(index).cancel(true);
            //五、判斷是否取消成功,
            if (cancel) {
                System.out.println("Cancel Order Succeded:" + index);
            } else {
                System.out.println("cancel Order Failed:" + index);
            }
            try {
                //六、在每兩個請求之間讓「邪惡」線程睡一會。
                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%,取決你的電腦配置.
相關文章
相關標籤/搜索