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("運行了"+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 + "]";
    }
    
    
    
    

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM