在项目开发中,碰到了一些耗时任务的问题.
需要使用多线程,本文在使用原生JDK7的情况下优化
纯异步方式可以使用Java8或者引入RxJava框架进行编程。
普通任务
同步任务流程图如下
假定场景如下:
- taskA 耗时1秒
- taskB 耗时2秒
- taskC 耗时3秒
通常的操作就是遍历任务
+执行任务
, 代码如下
// 任务A
Runnable taskA = ()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 任务B
Runnable taskB = ()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 任务C
Runnable taskC = ()->{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 任务组
Runnable [] taskArray = new Runnable[]{taskA,taskB,taskC};
/**
* 执行所有人物
*/
@Test
public void testSimple(){
Date start = new Date();
for (Runnable aTaskArray : taskArray) {
aTaskArray.run();
}
Date end = new Date();
System.out.println(end.getTime()-start.getTime());
}
完成任务耗时6
秒
多线程同步任务
为了提高运行效率,引入多线程,让等待的时间不再累计,而是合并。
最终执行的时间是耗时最长的任务
执行时间
如下图所示,多个任务同时执行
代码
为了简便起见,使用了上面的任务组taskArray
对象。
在实际使用中可以自己实现Runnable接口并传入CountDownLatch对象进行操作
/**
* 同步等待所有任务处理完成方案
*/
@Test
public void testSync() throws InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(taskArray.length);
ExecutorService executorService = Executors.newFixedThreadPool(5);
Date start = new Date();
for (Runnable aTaskArray : taskArray) {
executorService.execute(new Runnable() {
@Override
public void run() {
aTaskArray.run();
countDownLatch.countDown();
}
});
}
countDownLatch.await();
Date end = new Date();
System.out.println(end.getTime()-start.getTime());
}
执行时间: 3
秒
使用 JDK5 提供的 CountDownLatch 进行同步优化
优点
- 不用修改后续的代码(因为是同步的,后续的内容依赖于本次的内容也不影响)
缺点
- 会造成调用线程等待
多线程异步任务
/**
* 异步回调方案
* @throws InterruptedException
*/
@Test
public void testReturnAsync() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
Date start = new Date();
AtomicInteger atomicInteger = new AtomicInteger(taskArray.length);
for (Runnable aTaskArray : taskArray) {
executorService.execute(new Runnable() {
@Override
public void run() {
aTaskArray.run();
if(atomicInteger.decrementAndGet()==0){
Date end = new Date();
System.out.println(end.getTime()-start.getTime());
}
}
});
}
System.out.println("等待子线程的结果");
Thread.sleep(5*1000);
}
执行时间: 3
秒
优点
- 不会造成调用线程等待
缺点:
- 需要修改了法签名,需要传入Runnable对象,执行回调,后续的代码需要跟着修改
总结
- 使用多线程异步还是同步主要的决定因素在于:
调用线程是不是可以阻塞
- 多线程同步和多线程异步的执行效率没有差别