Java多執行緒——獲取多個執行緒任務執行完的時間

gary-liu發表於2017-06-24

問題

最近我在處理一批資料,用多執行緒來處理,我想知道大概多久能處理完。比如我先用多執行緒處理 100 條資料,統計下用時,然後根據總的資料量就可以大概估算出處理完這批資料要多久。

使用 CountDownLatch 計時

思路:用兩個 CountDownLatch 倒數計時鎖:開始計時鎖,任務結束計時鎖。開始計時鎖在子執行緒任務開始時通過 await() 阻塞所有子執行緒,然後在主執行緒中通過 CountDownLatch 控制所有子執行緒同時開始獲取開始時間;任務結束計時鎖 CountDownLatch 在每個子執行緒執行完後都 countDown 一次,直到所有子執行緒執行完,主執行緒開始記錄所有任務執行結束時間。

示例程式碼

/**
 * ClassName: ThreadTiming <br/>
 * Function: 計算多個執行緒任務執行完後的用時<br/>
 *
 * @author gary.liu
 * @date 2017/6/24
 */
public class ThreadTiming {

    private int nThread;

    private CountDownLatch startGate;
    private CountDownLatch endGate;

    public ThreadTiming(int nThread, CountDownLatch startGate, CountDownLatch endGate) {

        this.nThread = nThread;
        this.startGate = startGate;
        this.endGate = endGate;
    }

    class worker implements Runnable {

        public void run() {
            try {
                startGate.await();
                Random random = new Random();
                int num = random.nextInt(500) + 500;
                System.out.println(Thread.currentThread().getName() + " start and sleep: " + num + "ms");
                Thread.sleep(num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                endGate.countDown();
            }
        }
    }

    public long timeTasks() {

        for(int i = 0; i < nThread; i++){
            Thread thread = new Thread(new worker());
            thread.start();
        }

        long start = System.currentTimeMillis();
        //所有阻塞的任務同時開始
        startGate.countDown();
        try {
            //主執行緒阻塞,等待其他所有 worker 執行緒完成後再執行
            endGate.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("用時: " + (end - start) + "ms");

        return end - start;
    }

    public static void main(String[] args) {

        int nThread = 5;
        CountDownLatch startGate = new CountDownLatch(1);
        CountDownLatch endGate = new CountDownLatch(nThread);

        new ThreadTiming(nThread, startGate, endGate).timeTasks();

    }
}

執行結果

Thread-4 start and sleep: 897ms
Thread-0 start and sleep: 811ms
Thread-2 start and sleep: 678ms
Thread-3 start and sleep: 582ms
Thread-1 start and sleep: 576ms
用時: 903ms

可以看到總用時比花費最長時間的執行緒任務時間多一點,隨著併發量越大,達到可同時併發執行的執行緒最大數後,用時會越久。下面執行緒池的例子,限制了併發執行緒數後,可以明顯的看到這一點。

用柵欄 CyclicBarrier 應該也是可以實現的,也可以和wait()、notifyAll() 混用來實現 ,這裡就不在具體展開了。

使用執行緒池中方法計時

執行緒池中提供了監控執行緒池執行的一些方法,這裡通過執行緒池的 isTerminated() 方法不斷檢測,執行緒池中的任務是否都執行完成了,來獲取所有任務結束時間。

示例程式碼

public class ExecuteOrderPractice {

    public void orderPractice(){
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        long start = System.currentTimeMillis();
        for(int i = 0; i < 5; i++){
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try{
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + " do something");
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            });
        }

        executorService.shutdown();

        while(true){
            if(executorService.isTerminated()){
                //System.out.println("Finally do something ");
                long end = System.currentTimeMillis();
                System.out.println("用時: " + (end - start) + "ms");
                break;
            }

        }
    }

    public static void main(String[] args){
        new ExecuteOrderPractice().orderPractice();

    }
}

執行結果

pool-1-thread-1 do something
pool-1-thread-3 do something
pool-1-thread-2 do something
pool-1-thread-1 do something
pool-1-thread-3 do something
用時: 2010ms

參考資料

《Java 併發程式設計實戰》

相關文章