之前看的多线程案例都是利用System.out.println() 来进行输出。那种其实简化了很多
刚好最近有利用多线程进行网络访问。来提升效率。记录下
背景:我负责的项目有个接口一直特别慢。经过分析:慢的原因是因为循环调用了同一个接口。我这个接口有两部分:一部分查询数据库。另一部分从远程服务获取数据。
我分析后给出的方案是:处理到循环调用。
- 查询数据库部分 将之前的类型getUser(int id)这种查询改成
listUser(List<String>)这种批量查询 - 将对远程的访问利用多个线程并行执行。
最终结果证明的方案可行的。成功将接口响应时间降了下来
这里介绍利用多线程访问的案例
下面的原装代码示例。还是比较看懂的
原理:串行一个接一个的访问太消耗时间了。于是考虑利用线程并行执行
我这里每一批次大概有10组数据。同时我需要每次向数据返回。那种异步的我觉得不适合我这种场景,以及我也不擅长(我觉得前者是主要原因,因为没有合适的常见,所以不擅长)
- 我这里利用了ConcurrentLinkedQueue 来存储输入的参数。
- ConcurrentHashMap来存储每一批获取的结果。
- 然后CountDownLatch 来等待一批完成在进行下一步
具体的逻辑看代码把。我觉得还是结构还是比较清晰的
示例代码
构造一个单例的线程安全的线程池
package com.centanet.bizcom.thread;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
/**
* @Author: wbdengtt
* @Date: 2020/10/27 9:34
* 创建一个线程安全的 单例线程池
*/
public class ThreadPoolFactory {
private static ExecutorService executorService = null;
/**
* 获取CPU核心数
* IO密集型任务 = 一般为2*CPU核心数(常出现于线程中:数据库数据交互、文件上传下载、网络数据传输等等)
* CPU密集型任务 = 一般为CPU核心数+1(常出现于线程中:复杂算法)
* 混合型任务 = 视机器配置和复杂度自测而定
*/
private static final int CORE_SIZE = Runtime.getRuntime().availableProcessors();
private ThreadPoolFactory() {
}
// 创建一个线程安全的 单例线程池
public static synchronized ExecutorService getBizComThreadPool() {
if (executorService == null) {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("ThreadPool-%d").build();
executorService = new ThreadPoolExecutor(CORE_SIZE, CORE_SIZE * 2, 30L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), threadFactory);
}
return executorService;
}
}
线程池的使用
package com.centanet.bizcom.thread;
import com.centanet.bizcom.model.dto.KeyValueDTO;
import com.centanet.bizcom.model.vo.Pre400ParameterVO;
import com.centanet.bizcom.util.BizHelper;
import com.centanet.bizcom.util.CityHelper;
import com.centanet.bizcom.util.ConfigHelper;
import com.centanet.bizcom.util.HttpHelper;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
/**
* @Author: wbdengtt
* @Date: 2020/10/27 9:45
* 用来帮助获取400长短号。这个线程没有通用性。仅能用来获取400长短号
*/
@Getter
@Setter
@Slf4j
public class CallThreadHelper {
/**
* 用来等待线程完成的阑珊
*/
private CountDownLatch countDownLatch;
/**
* 用来接收结果
*/
private ConcurrentHashMap<String, String> resultMap;
/**
* 输入参数
*/
private ConcurrentLinkedQueue<KeyValueDTO> paramQueue;
public Map<String, String> getResult(List<Pre400ParameterVO> pre400ParameterVOS, String cityCode) {
paramQueue = new ConcurrentLinkedQueue<>();
resultMap = new ConcurrentHashMap<>(16);
for (Pre400ParameterVO pre : pre400ParameterVOS) {
if (pre == null || pre.getKey() == null || pre.getPostId() == null || pre.getStaff() == null) {
continue;
}
KeyValueDTO keyValueDTO = new KeyValueDTO(pre.getKey(), BizHelper.get400Param(pre), cityCode);
paramQueue.add(keyValueDTO);
}
int sizeTemp = paramQueue.size();
countDownLatch = new CountDownLatch(sizeTemp);
ExecutorService executorService = ThreadPoolFactory.getBizComThreadPool();
for (int i = 0; i < sizeTemp; i++) {
CallRunnable callRunnable = new CallRunnable();
executorService.execute(callRunnable);
}
// 等待所有线程完成
try {
countDownLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("await失败,请检查");
}
return resultMap;
}
class CallRunnable implements Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@Override
public void run() {
while (!paramQueue.isEmpty()) {
KeyValueDTO keyValueDTO = paramQueue.poll();
if (keyValueDTO == null) {
return;
}
// 获取远程结果,并将结果装载到resultMap
resultMap.put(keyValueDTO.getKey(), getPostResponse(keyValueDTO.getValue(), keyValueDTO.getCityCode()));
countDownLatch.countDown();
}
}
/**
* 获取400 结果 这里是结果获取的直接结果。没有任何处理
*
* @param jsonParam 输入参数
* @param cityCode 因为不能直接从CityHelper.getCityCode()获得cityCode。需要传递进来。不能获取的原因是这和controller那个已经不是一个线程了
* @return 返回结果
*/
private String getPostResponse(String jsonParam, String cityCode) {
String url = ConfigHelper.get400Url(cityCode);
try {
return HttpHelper.getPostResponse(jsonParam, url, CityHelper.getCityen(cityCode));
} catch (IOException e) {
log.error("从400获取长短号发生异常{},访问的地址为{},城市{} ", e, url, CityHelper.getCityen(cityCode));
}
// 补偿机制 通过域名再拿一次
try {
url = ConfigHelper.get400StandbyAddress(cityCode);
return HttpHelper.getPostResponse(jsonParam, url, CityHelper.getCityen(cityCode));
} catch (IOException e) {
log.error("从400获取长短号发生异常{},访问的地址为{},城市{} ", e, url, CityHelper.getCityen(cityCode));
}
//东西没拿到,向微信平台发送警报 这里关于警报的接口我这里无法调用。而且没必要。我后续根据结果是否是None来判断警报好了
return "None";
}
}
}

被折叠的 条评论
为什么被折叠?



