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