使用场景:需要批量处理数据,或者执行耗时任务,使用线程池处理,可提高效率,同时也方便对线程进行统一管理
创建线程池
// 创建一个并发数为10的线程池
ExecutorService mExecutor = Executors.newFixedThreadPool(10);
创建一个实体类,用来生成本次任务需要建立的数据
import java.math.BigDecimal;
public class DeductLog {
private String caseSn; // 案件号
private BigDecimal deductPmt;// 扣款金额
public String getCaseSn() {
return caseSn;
}
public void setCaseSn(String caseSn) {
this.caseSn = caseSn;
}
public BigDecimal getDeductPmt() {
return deductPmt;
}
public void setDeductPmt(BigDecimal deductPmt) {
this.deductPmt = deductPmt;
}
}
建立任务
这里的任务主要是针对单个的耗时任务
import java.math.BigDecimal;
import java.util.concurrent.Callable;
public class QuoteTask implements Callable<DeductLog> {
public String caseSn;
public QuoteTask(String caseSn) {
this.caseSn = caseSn;
}
@Override
public DeductLog call() throws Exception {
// 模拟耗时操作
Thread.sleep(100);
// 操作结束,得到我们想要的数据
DeductLog deductLog = new DeductLog();
deductLog.setCaseSn(caseSn);
deductLog.setDeductPmt(BigDecimal.TEN);
return deductLog;
}
}
提交任务
这里提交任务分为2种
invokeAll(tasks) 批量提交不限时任务
invokeAll(tasks, timeout, unit) 批量提交限时任务
InvokeAll方法处理一个任务的容器(collection),并返回一个Future的容器。两个容器具有相同的结构:
invokeAll将Future添加到返回的容器中,这样可以使用任务容器的迭代器,从而调用者可以将它表现的Callable与Future 关联起来。
当所有任务都完成时、调用线程被中断时或者超过时限时,限时版本的invokeAll都会返回结果。 超过时限后,任务尚未完成的任务都会被取消
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;
public class InvokeAllThread {
// 固定大小的线程池
static ExecutorService mExecutor = Executors.newFixedThreadPool(10);
/**
* 执行批量任务
* 多线程产生大批量资料
*
* @return
*/
public static List<DeductLog> getRankedTravelQuotes(List<QuoteTask> tasks) throws InterruptedException {
/**
* 使用invokeAll方法批量提交限时任务任务,预期15s所有任务都执行完,没有执行完的任务会自动取消
*/
// List<Future<DeductLog>> futures = mExecutor.invokeAll(tasks, 15, TimeUnit.SECONDS);
/**
* 使用不限时的任务
*/
List<Future<DeductLog>> futures = mExecutor.invokeAll(tasks);
// 需要返回的资料
List<DeductLog> deductLogList = new ArrayList<>();
Iterator<QuoteTask> taskIter = tasks.iterator();
for (Future<DeductLog> future : futures) {
QuoteTask task = taskIter.next();
try {
deductLogList.add(future.get());
} catch (ExecutionException e) {
e.printStackTrace();
System.out.println("任务异常,案件号是" + task.caseSn);
} catch (CancellationException e) {
e.printStackTrace();
System.out.println("任务超时,案件号是" + task.caseSn);
}
}
// 关闭线程池
mExecutor.shutdown();
return deductLogList;
}
}
测试
创建一个测试数据,丢到线程池里,使用不限时的方式执行
public static void main(String[] args) throws Exception {
// 创建批量任务
List<QuoteTask> tasks = new ArrayList<>();
for (int i = 1; i <= 200; i++) {
tasks.add(new QuoteTask(System.currentTimeMillis() + "" + i));
}
// 多线程跑任务
long start = System.currentTimeMillis();
List<DeductLog> deductLogList = InvokeAllThread.getRankedTravelQuotes(tasks);
long end = System.currentTimeMillis();
System.out.println("多线程处理结束:" + deductLogList.size());
System.out.println("多线程执行总耗时:" + (end - start) / 1000 + "s");
// 单线程跑任务
start = System.currentTimeMillis();
deductLogList = new ArrayList<>();
for (QuoteTask quoteTask : tasks) {
deductLogList.add(quoteTask.call());
}
end = System.currentTimeMillis();
System.out.println("单线程处理结束:" + deductLogList.size());
System.out.println("单线程执行总耗时:" + (end - start) / 1000 + "s");
}
结果
执行耗时结果如下图所示,由此可见,多线程的效率还是可以的
如果使用spring框架,推荐使用自带的线程池,参考