java 多線程併發實現(1)

背景

我們在開發中常常會遇到針對某個服務出現響應時間過長,用戶使用體驗感極差,出現這種問題的原因比較多,我們現在就說一下其中的一種情況,for循環遍歷查詢。

前提

首先先來簡單介紹一個多線程,提到多線程,首先要明白這幾個詞彙的意思,進程,線程,併發,並行,多線程,同步。

進程:簡單來講,就是一個正在進行中的程序,比如請求一次接口。

線程:進程中獨立運行的一個子任務就是一個線程,比如請求一次接口的過程就可以是一個線程,也可以啓動多個線程。

併發:指兩個或多個事件在同一時間段內執行。

並行:值兩個或多個時間在同一時間點執行。

同步:通過人爲的控制使多線程訪問公共資源時保證線程安全,比如在公共方法上加鎖(synchronized),線程安全是優化線程性能的前提。

優化前代碼

JSONArray devices = req.getJSONArray("devices");
if(null!=devices&&!devices.isEmpty()){    
    for(Object device:devices){
        saveTodayAICalculate(device);   //循環執行方法
    }    
}

//方法
public void saveTodayAICalculate(JSONObject device){
    //執行方法內容
    xxx...
}

優化後代碼

JSONArray devices = req.getJSONArray("devices");
if(null!=devices&&!devices.isEmpty()){
    try{
        ExecutorService es = Executors.newFixedThreadPool(8); //定義線程池
        CountDownLatch cdld = new CountDownLatch(devices.size());
        for(Object device:devices){
            es.execute(new Runnable(){
                @Override
                public void run() {
                          saveTodayAICalculate(device,cdld); //執行線程方法
                }
            });
        }
        cdld.await();
    }catch(Exception e){
        logger.error("execute executorServcice error,reason is {}",e);
    }finally{
        es.shutdown(); //執行完一定要銷燬線程,不然容易出現內存溢出異常
    }  
}

//線程執行方法
public void saveTodayAICalculate(JSONObject device,CountDownLatch cdl){
    try{
        xxx...
    }catch(Exception e){
        xxx...
    }finally{
        if(null!=cdl){
            cdl.countDown();
        }
    }
}

說明

1 通過java.util.cocarrent.Executors創建線程池ExecutorService,創建的方式有多種,這裏只使用newFixedThreadPool穿件固定大小線程池。

2 使用countDownLatch控制線程的執行。

3 多線程執行完一定要銷燬線程池,不然會出現內存溢出異常。

總結一下,這種多線程的缺陷一是當併發量多時,容易創建大量的線程池,佔用大量cpu和內存,二是當執行線程需要返回數據時不滿足,這種情況可以通過實現Callable接口,然後通過Future獲取返回值。比如:

ExecutorService es = Executors.newFixedThreadPool(8);
List<Future<String>> tasks = new ArrayList<>();
for(int i=0;i<12;i++){
    Future<String> future = es.submit(new MyCallable(i));
    tasks.add(future);
}
for(Future<String> future : tasks){
    System.out.println(future.get());
}

//線程類
public class MyCallable implements Callable<String> {
    private Integer k;
    public MyCallable(Integer k){
        this.k = k;
    }
    @Override
    public String call() throws Exception {
        return this.k +":"+ this.toString();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章