先来看一下改造前的模拟代码

这边模拟遍历一个大小是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秒时间

java 循环多线程池 java多线程遍历list_子线程

 

接下来我们采用多线程查询,直接在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倍??

java 循环多线程池 java多线程遍历list_List_02

 

我们来看一下查询之后的结果,继续在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

java 循环多线程池 java多线程遍历list_System_03

 

这是因为主线程在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秒快了很多

java 循环多线程池 java多线程遍历list_System_04

java 循环多线程池 java多线程遍历list_System_05

 

这边有一个需要注意的地方,那就是子线程异常的处理

首先我们在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 + "]";
    }
    
    
    
    

}