先来看一下改造前的模拟代码
这边模拟遍历一个大小是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("运行了"+deviceEntity.getDeviceId()+"天");
//模拟接口阻塞时间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);
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();
/*
* 这边为了方便演示,使用Executors.newFixedThreadPool(8)简单创建一个大小为8的线程池
* 生产代码中请使用ThreadPoolExecutor创建线程池
*
*/
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(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.getDeviceRunTime(deviceEntity);
}
}
再次运行ServiceDemo类的main方法,控制台输出如下。提升了2500倍??
我们来看一下查询之后的结果,继续在main方法中添加,遍历输出一下deviceEntities
public static void main(String[] args) {
List<DeviceEntity> deviceEntities = getAllDevices();
long currentTimeMillis = System.currentTimeMillis();
/*
* 这边为了方便演示,使用Executors.newFixedThreadPool(8)简单创建一个大小为8的线程池
* 生产代码中请使用ThreadPoolExecutor创建线程池
*
*/
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(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("运行了"+deviceEntity.getDeviceId()+"天");
//模拟接口阻塞时间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();
/*
* 这边为了方便演示,使用Executors.newFixedThreadPool(8)简单创建一个大小为8的线程池
* 生产代码中请使用ThreadPoolExecutor创建线程池
*
*/
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(8);
//注意创建CountDownLatch时的大小和deviceEntities大小一致
final CountDownLatch countDownLatch = new CountDownLatch(deviceEntities.size());
ChildThread.countDownLatch=countDownLatch;
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.getDeviceRunTime(deviceEntity);
//计数器减一
countDownLatch.countDown();
}
}
再运行一下ServiceDemo类的main方法,输出结果如下
这次全部runTime都有了,整个查询是1305毫秒,相比之前的10秒快了很多
这边有一个需要注意的地方,那就是子线程异常的处理
首先我们在getDeviceRunTime中加两行代码,拋个异常
//模拟根据设备信息获取设备运行时间
public static void getDeviceRunTime(DeviceEntity deviceEntity) {
if(deviceEntity.getDeviceId()/50>=1){
throw new NullPointerException();
}
//模拟接口阻塞时间100毫秒
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
deviceEntity.setRunTime("运行了"+deviceEntity.getDeviceId()+"天");
}
再运行一下ServiceDemo类的main方法,会发现等到天荒地老控制台也不会输出
先说明一下原因,因为子线程的run()方法中countDownLatch.countDown()是在调用getDeviceRunTime()方法之后
而getDeviceRunTime()方法现在有异常抛出了,根据上面的异常逻辑,第50个线程开始会拋异常,那就是countDownLatch.countDown()只会执行49次
那么主线程的计算器就停留在51了,主线程将一直处于阻塞等待的状态
@Override
public void run() {
ServiceDemo.getDeviceRunTime(deviceEntity);
//计数器减一
countDownLatch.countDown();
}
把子线程的run方法改一下,捕获一下异常,把countDownLatch.countDown()写在finally里,这样可以保证计数器最终是0
@Override
public void run() {
try{
ServiceDemo.getDeviceRunTime(deviceEntity);
}catch(Exception e){
System.out.println("子线程异常:"+e);
}finally{
//计数器减一
countDownLatch.countDown();
}
}
下面是最终的代码
一:ChildThread
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() {
try{
ServiceDemo.getDeviceRunTime(deviceEntity);
}catch(Exception e){
System.out.println("子线程异常:"+e);
}finally{
//计数器减一
countDownLatch.countDown();
}
}
}
二:ServiceDemo
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 main(String[] args) {
List<DeviceEntity> deviceEntities = getAllDevices();
long currentTimeMillis = System.currentTimeMillis();
/*
* 这边为了方便演示,使用Executors.newFixedThreadPool(8)简单创建一个大小为8的线程池
* 生产代码中请使用ThreadPoolExecutor创建线程池
*
*/
ThreadPoolExecutor newFixedThreadPool=new ThreadPoolExecutor(8, 8, 1000L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(200), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
//ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(8);
//注意创建CountDownLatch时的大小和deviceEntities大小一致
final CountDownLatch countDownLatch = new CountDownLatch(deviceEntities.size());
ChildThread.countDownLatch=countDownLatch;
for (DeviceEntity deviceEntity : deviceEntities) {
ChildThread childThread = new ChildThread(deviceEntity);
newFixedThreadPool.submit(childThread);
}
try {
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 static void getDeviceRunTime(DeviceEntity deviceEntity) {
if(deviceEntity.getDeviceId()/50>=1){
throw new NullPointerException();
}
//模拟接口阻塞时间100毫秒
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
deviceEntity.setRunTime("运行了"+deviceEntity.getDeviceId()+"天");
}
//假设有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);
deviceEntity.setDeviceIp("192.168.100."+i);
deviceEntities.add(deviceEntity);
}
return deviceEntities;
}
}
三:DeviceEntity
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 + "]";
}
}