java多线程遍历list元素,作为参数查询接⼝的改造过程先来看⼀下改造前的模拟代码
这边模拟遍历⼀个⼤⼩是100的list,遍历每个元素去查询运⾏时间
public class ServiceDemo {
public static void main(String[] args) {
List<DeviceEntity> deviceEntities=getAllDevices();
long currentTimeMillis = System.currentTimeMillis();
for (DeviceEntity deviceEntity : deviceEntities) {
getDeviceRunTime(deviceEntity);
}
long currentTimeMillis2 = System.currentTimeMillis();
System.out.println("查询了"+(currentTimeMillis2-currentTimeMillis)+"毫秒");
}
//模拟根据设备信息获取设备运⾏时间
public static void getDeviceRunTime(DeviceEntity deviceEntity) {
deviceEntity.setRunTime("运⾏了"+DeviceId()+"天");
//模拟接⼝阻塞时间100毫秒
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//假设有100台设备
public static List<DeviceEntity> getAllDevices() {
List<DeviceEntity> deviceEntities=new ArrayList<>();
for (int i = 1; i <= 100; i++) {
DeviceEntity deviceEntity=new DeviceEntity();
deviceEntity.setDeviceId(i);
deviceEntity.setDeviceName("设备"+i);
vue json字符串转数组deviceEntity.setDeviceIp("192.168.100."+i);
deviceEntities.add(deviceEntity);
}
return deviceEntities;
}
}
实体类代码如下:
public class DeviceEntity {
private int deviceId;
private String deviceName;
private String deviceIp;
private String runTime;
public int getDeviceId() {
return deviceId;
}
public void setDeviceId(int deviceId) {
this.deviceId = deviceId;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceIp() {
return deviceIp;
}
public void setDeviceIp(String deviceIp) {
this.deviceIp = deviceIp;
public String getRunTime() {
return runTime;
}
public void setRunTime(String runTime) {
this.runTime = runTime;
}
@Override
public String toString() {
return "DeviceEntity [deviceId=" + deviceId + ", deviceName=" + deviceName + ", deviceIp=" + deviceIp
+ ", runTime=" + runTime + "]";
}
运⾏ServiceDemo类的main⽅法,控制台输出如下,因为模拟具体的查询接⼝的阻塞时间是100毫秒,那么100次查询也就是10秒时间
接下来我们采⽤多线程查询,直接在ServiceDemo类的main⽅法中改造
public static void main(String[] args) {
List<DeviceEntity> deviceEntities = getAllDevices();
long currentTimeMillis = System.currentTimeMillis();
/*
* 这边为了⽅便演⽰,使⽤wFixedThreadPool(8)简单创建⼀个⼤⼩为8的线程池
* ⽣产代码中请使⽤ThreadPoolExecutor创建线程池
*
*/
ExecutorService newFixedThreadPool = wFixedThreadPool(8);
for (DeviceEntity deviceEntity : deviceEntities) {
ChildThread childThread = new ChildThread(deviceEntity);
newFixedThreadPool.submit(childThread);
}
newFixedThreadPool.shutdown();
long currentTimeMillis2 = System.currentTimeMillis();
System.out.println("查询了" + (currentTimeMillis2 - currentTimeMillis) + "毫秒");
}
⼦线程对应代码
public class ChildThread implements Runnable{
private DeviceEntity  deviceEntity;
public ChildThread(DeviceEntity deviceEntity) {
super();
this.deviceEntity = deviceEntity;
}
@Override
public void run() {
}
}
再次运⾏ServiceDemo类的main⽅法,控制台输出如下。提升了2500倍??
我们来看⼀下查询之后的结果,继续在main⽅法中添加,遍历输出⼀下deviceEntities
public static void main(String[] args) {
List<DeviceEntity> deviceEntities = getAllDevices();
long currentTimeMillis = System.currentTimeMillis();
/*
* 这边为了⽅便演⽰,使⽤wFixedThreadPool(8)简单创建⼀个⼤⼩为8的线程池
* ⽣产代码中请使⽤ThreadPoolExecutor创建线程池
*
*/
ExecutorService newFixedThreadPool = wFixedThreadPool(8);
for (DeviceEntity deviceEntity : deviceEntities) {
ChildThread childThread = new ChildThread(deviceEntity);
newFixedThreadPool.submit(childThread);
}
/
/线程池使⽤完后需⼿动关闭
newFixedThreadPool.shutdown();
long currentTimeMillis2 = System.currentTimeMillis();
System.out.println("查询了" + (currentTimeMillis2 - currentTimeMillis) + "毫秒");
for (DeviceEntity deviceEntity : deviceEntities) {
System.out.println("查询后的deviceEntity:" + deviceEntity);
}
}
发现只有前⼋条数据是有runtime,其余的是null
这是因为主线程在for循环遍历完之后就结束了,这边有⼋条结果是因为下⾯⽅法中Thread.sleep(100L)在setRunTime()⽅法之后
也就是在4毫秒时间⾥有⼋个线程已经执⾏了setRunTime()⽅法,这边如果把Thread.sleep(100L)⽅法放在setRunTime()⽅法之前,那么上⾯遍历结果中所有runTime都是null
//模拟根据设备信息获取设备运⾏时间
public static void getDeviceRunTime(DeviceEntity deviceEntity) {
deviceEntity.setRunTime("运⾏了"+DeviceId()+"天");
//模拟接⼝阻塞时间100毫秒
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
接下来让主线程等待所有⼦线程执⾏完毕再往下执⾏,使⽤CountDownLatch 来实现
第⼀改造⼀下ServiceDemo类的main⽅法
public static void main(String[] args) {
List<DeviceEntity> deviceEntities = getAllDevices();
long currentTimeMillis = System.currentTimeMillis();
/*
* 这边为了⽅便演⽰,使⽤wFixedThreadPool(8)简单创建⼀个⼤⼩为8的线程池        * ⽣产代码中请使⽤ThreadPoolExecutor创建线程池
*
*/
ExecutorService newFixedThreadPool = wFixedThreadPool(8);
//注意创建CountDownLatch时的⼤⼩和deviceEntities⼤⼩⼀致
final CountDownLatch countDownLatch = new CountDownLatch(deviceEntities.size());
for (DeviceEntity deviceEntity : deviceEntities) {
ChildThread childThread = new ChildThread(deviceEntity);
newFixedThreadPool.submit(childThread);
}
try {
//计数器等待⼦线程减为0之后才往下执⾏
countDownLatch.await();
} catch (InterruptedException e) {
/
/ TODO Auto-generated catch block
e.printStackTrace();
}finally{
newFixedThreadPool.shutdown();
}
long currentTimeMillis2 = System.currentTimeMillis();
System.out.println("查询了" + (currentTimeMillis2 - currentTimeMillis) + "毫秒");
for (DeviceEntity deviceEntity : deviceEntities) {
System.out.println("查询后的deviceEntity:" + deviceEntity);
}
}
第⼆步:改造⼦线程类
public class ChildThread implements Runnable{
private DeviceEntity  deviceEntity;
//添加静态属性CountDownLatch
public static CountDownLatch countDownLatch = null;
public ChildThread(DeviceEntity deviceEntity) {
super();
this.deviceEntity = deviceEntity;
}
@Override
public void run() {
//计数器减⼀
}
}
再运⾏⼀下ServiceDemo类的main⽅法,输出结果如下
这次全部runTime都有了,整个查询是1305毫秒,相⽐之前的10秒快了很多
这边有⼀个需要注意的地⽅,那就是⼦线程异常的处理
⾸先我们在getDeviceRunTime中加两⾏代码,拋个异常
//模拟根据设备信息获取设备运⾏时间
public static void getDeviceRunTime(DeviceEntity deviceEntity) {
DeviceId()/50>=1){
throw new NullPointerException();
}
//模拟接⼝阻塞时间100毫秒
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
deviceEntity.setRunTime("运⾏了"+DeviceId()+"天");
}
再运⾏⼀下ServiceDemo类的main⽅法,会发现等到天荒地⽼控制台也不会输出
先说明⼀下原因,因为⼦线程的run()⽅法中untDown()是在调⽤getDeviceRunTime()⽅法之后
⽽getDeviceRunTime()⽅法现在有异常抛出了,根据上⾯的异常逻辑,第50个线程开始会拋异常,那就是untDown()只会执⾏49次
那么主线程的计算器就停留在51了,主线程将⼀直处于阻塞等待的状态
@Override
public void run() {
//计数器减⼀
}
把⼦线程的run⽅法改⼀下,捕获⼀下异常,把untDown()写在finally⾥,这样可以保证计数器最终是0
@Override
public void run() {
try{
}catch(Exception e){
System.out.println("⼦线程异常:"+e);
}finally{
//计数器减⼀
}
}
下⾯是最终的代码
⼀:ChildThread

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。