一种多线程设计思路
Posted helloworldplus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一种多线程设计思路相关的知识,希望对你有一定的参考价值。
一、为什么设计多线程
举个例子说明多线程的作用。比如有100个人去食堂打菜,如果只有一个窗口,那么所有人都需要在这个窗口进行排队,一个打完才能排到下一个,如果每个人打菜需要1分钟,这100个人打完菜总耗时就是100分钟,这就类似程序中的单线程。如果有5个打菜窗口,那么就可以每次5个人几乎同时进行打菜,相当于把100个人分成5个队,这样打菜的时间就减少了5倍,这100个人打完菜总耗时差不多为20分钟。这就类似于程序中使用了多线程。所以,使用多线程对用户体验的提升毋庸置疑。
类似,物业软件中也有很多场景可以使用多线程。如多线程远程开门(同时开整个小区所有的门)、多线程下载卡、多线程下载设备参数、多线程删除卡等。
二、多线程的设计思路
本文旨在设计一个通用型的多线程Http客户端。既然是通用,肯定是要兼容各种数据类型,而json字符串恰好能承担这个重任。另一方面,几乎所有的业务执行都需要得到执行结果反馈,故多线程的设计还需要考虑业务上的阻塞。这篇多线程程序阻塞核心算法如下:
public List<String> send() { for(String data : jsonArray) { threadPool.execute(new MultiThread(url, data)); } threadPool.shutdown(); while (!threadPool.isTerminated()) { } return jsonArrayResponse; }
从上述程序可看出,多线程的创建及执行使用了线程池做管理,而ExecutorService中的shutdown和isTerminated方法结合起来就可以实现多线程阻塞。
其次,对于某些多线程业务,可能还需要依赖外部资源。如物业软件的门禁记录转发,由于设备门禁记录和抓拍的图片数据是异步上传的,通常记录会先到,抓拍的图片会延迟一两秒才上报完成。而转发给第三方的门禁数据中需要包含抓拍图片,那怎么办呢?本文同样提供了解决办法,如下:
(1)转发门禁记录时,使用LockUtil的lock方法(key为图片路径)将转发线程锁住。
(2)图片数据上报到物业中心时,先使用LockUtil的setParam方法把图片数据设置到缓存中,再使用LockUtil的unlock方法(key为图片路径)将图片解锁。
(3)解锁后的门禁记录线程,使用LockUtil的getParma方法把图片数据从缓存中读出来,并添加到记录的属性中,就可了转发一条完整的门禁记录到第三方了。
核心算法如下:
@Override public void run() { super.run(); // 若线程有锁,此处处理线程锁中的参数。 if (hasLock) { // 使用LockUtil的lock方法(key为图片路径)将转发线程锁住 LockUtil.getInstance().lock(lockKey, 20); // 使用LockUtil的getParma方法把图片数据从缓存中读出来 String param = (String)LockUtil.getInstance().getParam(lockKey); // 把参数添加到记录的属性中 if (StringUtils.isNotBlank(param)) { JSONObject obj = JSON.parseObject(data); obj.put(lockParamKey, param); data = JSON.toJSONString(obj); } } String res = HttpRequest.post(url).body(data).execute().body(); jsonArrayResponse.add(res); }
上述设计方法看似已经没什么问题,其实不然,由于应答数据是从多线程中逐条添加的,并且使用了ArrayList,而ArrayList是线程不安全的(ArrayList线程为什么不安全,请移步到https://blog.csdn.net/u012859681/article/details/78206494)。要解决ArrayList的线程安全问题其实很简单,只需要将多线程中的ArrayList改成如下写法即可:
List<String> jsonArrayResponse = Collections.synchronizedList(new ArrayList<>());
三、程序摘要
package com.leelen.ehome.multithread; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.leelen.ehome.utils.LockUtil; import com.xiaoleilu.hutool.http.HttpRequest; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Http多线程客户端 * * @Author:Jack */ public class HttpMultithreadClient { /** * json字符串数组,即需要发送的数据。 */ private List<String> jsonArray; /** * 发送目的地址:url */ private String url; /** * 是否对线程加锁,默认否 */ private boolean hasLock = false; /** * key,线程锁秘钥 */ private String lockKey; /** * 从锁中读出的参数名称 */ private String lockParamKey; /** * json格式应答数组数据 */ private List<String> jsonArrayResponse = Collections.synchronizedList(new ArrayList<>()); /** * 线程池 */ private ExecutorService threadPool = Executors.newFixedThreadPool(10); /** * * @param jsonArray json格式数组(数据体) * @param url 发送地址url * @param hasLock 是否加锁,若加锁则继续处理后两个参数 * @param lockKey 若加锁则该参数必填,否则可为null * @param lockParamKey 若加锁则该参数必填,否则可为null */ public HttpMultithreadClient(List<String> jsonArray, String url, boolean hasLock, String lockKey, String lockParamKey) { this.jsonArray = jsonArray; this.url = url; this.hasLock = hasLock; this.lockKey = lockKey; this.lockParamKey = lockParamKey; } /** * 多线程发送 * @return */ public List<String> send() { for(String data : jsonArray) { threadPool.execute(new MultiThread(url, data)); } threadPool.shutdown(); while (!threadPool.isTerminated()) { // 业务上需要等待执行结果,故此处多线程创建完毕后,需要阻塞等待执行结果的加载。 } return jsonArrayResponse; } /** * 多线程实现类 */ class MultiThread extends Thread { /** * 发送目的地址:url */ private String url; /** * 数据 */ private String data; public MultiThread(String url, String data) { this.url = url; this.data = data; } @Override public void run() { super.run(); // 若线程有锁,此处处理线程锁中的参数。 if (hasLock) { LockUtil.getInstance().lock(lockKey, 20); String param = (String)LockUtil.getInstance().getParam(lockKey); if (StringUtils.isNotBlank(param)) { JSONObject obj = JSON.parseObject(data); obj.put(lockParamKey, param); data = JSON.toJSONString(obj); } } String res = HttpRequest.post(url).body(data).execute().body(); jsonArrayResponse.add(res); } } }
以上是关于一种多线程设计思路的主要内容,如果未能解决你的问题,请参考以下文章