java 多线程处理list集合数据的实例应用
众所周知创建线程的三种方式:
- 继承Thread,重写run方法
- 实现Runnable接口,重新run方法
- 实现Callable接口,重写call方法
下面使用Callable,来说一下为什么使用
1.Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值。没有返回值这点稍微有点麻烦。不能声明抛出检查型异常则更麻烦一些。
2.public void run()方法契约意味着你必须捕获并处理检查型异常。即使你小心地保存了异常信息(在捕获异常时)以便稍后检查,但也不能保证这个类(Runnable对象)的所有使用者都读取异常信息。
3.你也可以修改Runnable实现的getter,让它们都能抛出任务执行中的异常。但这种方法除了繁琐也不是十分安全可靠,你不能强迫使用者调用这些方法,程序员很可能会调用join()方法等待线程结束然后就不管了
但是现在不用担心了,以上的问题终于在1.5中解决了。Callable接口和Future接口的引入以及他们对线程池的支持优雅地解决了这两个问题。
以上来自于https://blog.csdn.net/qq_37338761/article/details/89251663
Runnable是出自jdk1.0,Callable出自jdk1.5,那么,后出的类肯定对于前者有增强。
Callable接口实际上是属于Executor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable更加强大的功能。
- Callable可以在任务结束的时候提供一个返回值,Runnable无法提供这个功能
- Callable的call方法分可以抛出异常,而Runnable的run方法不能抛出异常。
- 运行Callable任务可拿到一个Future对象。
直接上代码
/** * 多线程处理list * * @param list 处理的数据 * @param errorMsg 业务阻断描述 * @return resultRecipientList 处理后的数据 * @date 2022/7/13 16:56 */ private List<RecipientInfoDTO> multiThreading(List<RecipientInfoDTO> list, List<String> errorMsg) { // 返回的数据 List<RecipientInfoDTO> resultRecipientList = Lists.newArrayList(); // 每10条数据开启一条线程 int threadSize = 10; // 总数据条数 int dataSize = list.size(); // 线程数 int threadNum = dataSize / threadSize + 1; // 计数器 CountDownLatch countDownLatch = new CountDownLatch(threadNum); // 定义标记,过滤threadNum为整数 boolean special = dataSize % threadSize == 0; // 创建一个线程池 ExecutorService exec = Executors.newFixedThreadPool(threadNum); // 定义一个任务集合 List<Callable<Boolean>> tasks = Lists.newArrayList(); // 定义一个任务 Callable<Boolean> task ; // 定义循环处理的i批次数据 List<RecipientInfoDTO> loopDataList; // 确定每条线程的数据 for (int i = 0; i < threadNum; i++) { if (i == threadNum - 1) { if (special) { countDownLatch.countDown(); break; } loopDataList = list.subList(threadSize * i, dataSize); } else { loopDataList = list.subList(threadSize * i, threadSize * (i + 1)); } // 当前循环处理的数据集 List<RecipientInfoDTO> recipientInfoDTOS = loopDataList; task = new Callable<Boolean>() { @Override public Boolean call() throws Exception { if (ObjectUtils.isNotEmpty(recipientInfoDTOS)) { for (RecipientInfoDTO recipientInfo : recipientInfoDTOS) { // do something 业务逻辑处理 // 校验收件地址是否停止服务 YtoVerifyOrderDTO ytoVerifyOrderDTO = getYtoVerifyOrderDTO(recipientInfo); if (ytoService.verifyReceivesAddress(ytoVerifyOrderDTO)) { recipientInfo.setMsg("当前收件地址的服务网点暂时停止服务"); recipientInfo.setUsable(false); errorMsg.add("当前收件地址的服务网点暂时停止服务"); continue; } // 通过校验,返回 resultRecipientList.add(recipientInfo); } } return true; } }; // 减少计数器的计数,如果计数达到零,则释放所有等待线程。 // 如果当前计数大于零,则递减。如果新计数为零,则重新启用所有等待线程以进行线程调度。 countDownLatch.countDown(); // 任务处理完加入集合 tasks.add(task); } try { // 执行给定的任务,返回一个 Futures 列表,在所有完成时保存它们的状态和结果。 // Future.isDone对于返回列表的每个元素都是true 。 // 请注意,已完成的任务可能已经正常终止,也可能通过引发异常终止。 // 如果在此操作进行时修改了给定的集合,则此方法的结果是不确定的 exec.invokeAll(tasks); // 等待计数器归零 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // 关闭线程池 exec.shutdown(); return resultRecipientList; }