java guava 多线程,SpringBoot多线程,使用Guava的ListenableFuture替代Java原生Future,多个线程同时异步回调!...

文章借鉴自:https://cloud.tencent.com/developer/article/1386118

由于不想粘自己代码,所以偷个懒,感觉原作者的代码没有注释,看起来很难受,所以将自己理解的重新发一下。

如有侵权请告知。

前提:

日常开发,有很多场景会使用到多线程,比如,我们解析Excel,如果解析出一个3万条数据的Excel表格,需要两部:

1.我们需要先异步解析出所有的数据,前面写过了如何异步处理任务(Spring Boot---(4)SpringBoot异步处理任务);

2.然后再多线程去处理业务或者插入到数据库;

这里,讲解一下,如何使用多线程,如何调用回调函数。

1.引入pom.xml的依赖

由于后面要用到Guava包下的东西,所以这里先引入一下。

com.google.guava

guava

22.0

最新好像已经更新到27.1了,没有去留意更新了哪些东东,有心的同学可以百度搜一下,都更新了些什么。

2.controller

这里模拟10万条数据,然后多线程处理。

package com.jd.concurrent;

import com.google.common.collect.ImmutableList;

import com.google.common.util.concurrent.*;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;

import java.util.*;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

/**

* author:lightClouds917

* date:2018/1/22

* description:模拟多线程处理

*/

@RestController

@RequestMapping("con")

public class ConController {

private final static Logger logger = LoggerFactory.getLogger(Logger.class);

// 线程池

private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 50, 500,

TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<>(200),

new ThreadPoolExecutor.CallerRunsPolicy()

);

//使用Guava的ListeningExecutorService装饰线程池

ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);

@RequestMapping(value = "test1", method = RequestMethod.GET)

public String test1() {

try {

//10万条数据

List list = new ArrayList<>();

List list2 = new ArrayList<>();

for (int i = 1; i <= 100000; i++) {

list.add("test:" + i);

}

//每条线程处理的数据尺寸

int size = 250;

int count = list.size() / size; //根据size得出线程数量

if (count * size != list.size()) {

count++; //如果已有线程要处理数据的总数,不等于list数据集合的总数,线程+1

}

int countNum = 0;//统计处理数据

final CountDownLatch countDownLatch = new CountDownLatch(count);//线程计数器

while (countNum < list.size()) {

countNum += size;

//创建一个对象,此对象继承Callable,下面会有源代码

ConCallable callable = new ConCallable();

//截取list的数据,分给不同线程处理

/*这段代码写的很好,我喜欢这段,根据集合的下标,形成多线程,每个线程处理固定的数量,当最后一个线程要处理的数据大于总数的时候,

则从上一个线程处理的末尾,到数据总数。真正意义上的多线程,本来多线程这块儿我是写死的,手动分配几个线程,代码效率低;

这段儿代码,根据size可以随时对线程调优,仅需修改size,即可找到适合自己业务的线程数。*/

callable.setList(ImmutableList.copyOf(list.subList(countNum - size, countNum < list.size() ? countNum : list.size())));

//执行线程

ListenableFuture listenableFuture = listeningExecutorService.submit(callable);

//异步回调操作,原作者仅仅是展示下如何使用回调,

/*如果有这种需求:多个线程执行,

都执行完毕,进行回调,则需要调用Futures.allAsList(futureList),多线程同时回调的代码会在文章末尾 单独贴出来。*/

Futures.addCallback(listenableFuture, new FutureCallback>() {

@Override

public void onSuccess(List list1) {

countDownLatch.countDown();//计数器-1

list2.addAll(list1);//将线程执行结果放入结果集

}

@Override

public void onFailure(Throwable throwable) {

countDownLatch.countDown();

logger.info("处理出错:", throwable);

}

});

}

//主线程阻塞,我直接这么用的countDownLatch.await();

// 原作者这个应该是个超时策略,超过这个时间的线程,直接舍弃。

countDownLatch.await(30, TimeUnit.MINUTES);

logger.info("符合条件的返回数据个数为:" + list2.size());

logger.info("回调函数:" + list2.toString());

} catch (Exception ex) {

ex.printStackTrace();

}

return "正在处理......";

}

}

3.线程任务处理类

package com.jd.concurrent;

import java.util.ArrayList;

import java.util.concurrent.Callable;

import java.util.List;

/**

* author:lightClouds917

* date:2018/1/22

* description:业务处理

*/

public class ConCallable implements Callable {

private List list;

@Override

public Object call() throws Exception {

List listRe = new ArrayList<>();

for(int i = 0;i < list.size();i++){

//含有‘4599’的字符串都返回

if(list.get(i).contains("4599")){

listRe.add(list.get(i));

}

}

return listRe;

}

public void setList(List list) {

this.list = list;

}

}

4.返回结果:

符合条件的返回数据个数为:20

回调函数:[test:4599, test:14599, test:24599, test:34599, test:44599, test:45990, test:45991, test:45992, test:45993, test:45994, test:45995, test:45996, test:45997, test:45998, test:45999, test:54599, test:64599, test:74599, test:84599, test:94599]

原作者的文章到此结束,但是没有达到我的目的呀,我希望的是多线程执行完毕,进行统一回调,为了达到这个目的,对代码进行了如下改造:需要调用Futures.allAsList()

//在循环外创建一个list数组,用于存放线程

List futureList = new ArrayList();

while (countNum < list.size()) {

countNum += size;

//创建一个对象,此对象继承Callable

ConCallable callable = new ConCallable();

//截取list的数据,分给不同线程处理

callable.setList(ImmutableList.copyOf(list.subList(countNum - size, countNum < list.size() ? countNum : list.size())));

//执行线程

ListenableFuture listenableFuture = listeningExecutorService.submit(callable);

//将子线程添加至线程集合

futureList.add(listenableFuture);

}

/*都执行完毕,进行回调,则需要调用Futures.allAsList(futureList),多线程同时回调.

* 由于是所有的线程一起回调,线程的返回结果自动存放在一个list中,

* 因此需要将上面的List 改为:List>*/

Futures.allAsList(listenableFuture, new FutureCallback>>() {

@Override

public void onSuccess(List> list1) {

for (List list : list1) {

countDownLatch.countDown();//计数器-1

list2.addAll(list);//将线程执行结果放入结果集

}

}

@Override

public void onFailure(Throwable throwable) {

countDownLatch.countDown();

logger.info("处理出错:", throwable);

}

});

//主线程阻塞

countDownLatch.await();

System.out.println("OK!");

大功告成!继续码代码!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值