Java多线程——获取多个线程任务执行完的时间

时间:2021-08-25 07:54:29

问题

最近我在处理一批数据,用多线程来处理,我想知道大概多久能处理完。比如我先用多线程处理 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 并发编程实战》