JAVA並行異步編程,線程池+FutureTask(分享來自推酷)

JAVA並行異步編程,線程池+FutureTask

java 在JDK1.5中引入一個新的併發包java.util.concurrent 該包專門爲java處理併發而書寫。

在java中熟悉的使用多線程的方式爲兩種?繼續Thread類,實現Runnale。兩種方式簡單方便。

在Jdk1.5之後其實有第三種方式實現方式,採用併發包中的 Callable接口 FuruteTask類 以及 ExecutorService接口。

說新的實現方式之前先來說討論一下傳統的java執行過程

首先一個簡單的程序一個方法生成隨機數,在生成隨機數的方法執行中,睡眠1s模擬方法調用時候的耗時,把結果放進集合中,最後算到總結果。

public 
  class Count{
    public static void main(String[] args) throws InterruptedException {
      long start = System.currentTimeMillis();
           Count count = new Count();
           List<Integer> res = new ArrayList<>();
           res.add(count.random());
           res.add(count.random());
           res.add(count.random());
           res.add(count.random());
           int totle =0;
           for (int i = 0; i < res.size(); i++) {
             totle+=res.get(i);
           }
          long end = System.currentTimeMillis();
           System.out.println("運算結束 耗時:"+(end-start)+"ms  totle:"+totle );
           System.out.println("退出main線程!");
         }
    int random() throws InterruptedException{
      Thread.sleep(1000); //
      return new Random().nextInt(100);
    }
  }

    結果如下

運算結束 耗時:4000ms  totle:66
退出main線程!

     在傳統的編寫中是單線程的操作,串行操作,當調用方法count.random(),main線程被阻塞起來,直到睡眠時間到達,自動喚醒main線程。

 

那麼有沒有什麼辦法來減少main主線程的阻塞時間呢?能不能讓這幾個操作並行進行呢?如果是並行運行帶來什麼好處呢?

並行帶來的好處,可以減少比較多的方法執行時間,如random()方法並行計算,也就是說main線程的阻塞只有1s,阻塞時間減少75%

 

java爲我們提供了多線程機制,利用多線程我們可以實現方法的並行運算,實現多線程的辦法,實現Runnable接口重新run,繼承Thread 重寫run;因爲run方法的並沒有返回值,我們手動的去創建大量的線程並且維護線程是件很討厭的事情,並且創建線程也是非常耗費資源的操作,能不能有一個池子來幫我們管理線程呢?有沒有一個類能夠透明的去進行透明併發的異步操作呢?這個在JDK1.5之前是沒有的,在1,5之後出現了一個新包,專門爲併發而開發的包,使用併發包中提供的類和接口,將很輕易的實現。併發編程。

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

 public class TestMain {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
    new  TestMain().exec();
  }
  void exec() throws InterruptedException, ExecutionException{
    //進行異步任務列表
    List<FutureTask<Integer>> futureTasks = new ArrayList<FutureTask<Integer>>();
    //線程池 初始化十個線程 和JDBC連接池是一個意思 實現重用 
    ExecutorService executorService = Executors.newFixedThreadPool(10);
    long start = System.currentTimeMillis();
    //類似與run方法的實現 Callable是一個接口,在call中手寫邏輯代碼
    Callable<Integer> callable = new Callable<Integer>() {
      @Override
      public Integer call() throws Exception {
        Integer res = new Random().nextInt(100);
        Thread.sleep(1000);
        System.out.println("任務執行:獲取到結果 :"+res);
        return  res;
      }
    };
    
    for(int i=0;i<10;i++){
      //創建一個異步任務
      FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
      futureTasks.add(futureTask);
      //提交異步任務到線程池,讓線程池管理任務 特爽把。
             //由於是異步並行任務,所以這裏並不會阻塞
      executorService.submit(futureTask); 
    }
    
    int count = 0;
       for (FutureTask<Integer> futureTask : futureTasks) {
         //futureTask.get() 得到我們想要的結果 
         //該方法有一個重載get(long timeout, TimeUnit unit) 第一個參數爲最大等待時間,第二個爲時間的單位
         count+= futureTask.get();
  }
     long end = System.currentTimeMillis();
     System.out.println("線程池的任務全部完成:結果爲:"+count+",main線程關閉,進行線程的清理");
     System.out.println("使用時間:"+(end-start)+"ms");
     //清理線程池 
     executorService.shutdown();
    
  }
}

上述情況如果不用異步並行,程序將至少睡眠10s

使用之後的結果

任務執行:獲取到結果 :99
任務執行:獲取到結果 :78
任務執行:獲取到結果 :52
任務執行:獲取到結果 :78
任務執行:獲取到結果 :97
任務執行:獲取到結果 :8
任務執行:獲取到結果 :97
任務執行:獲取到結果 :3
任務執行:獲取到結果 :78
任務執行:獲取到結果 :31
線程池的任務全部完成:結果爲:621main線程關閉,進行線程的清理
使用時間:1004ms 

我們試着把線程池的大小減少一半

任務執行:獲取到結果 :87
任務執行:獲取到結果 :60
任務執行:獲取到結果 :13
任務執行:獲取到結果 :18
任務執行:獲取到結果 :8
任務執行:獲取到結果 :86
任務執行:獲取到結果 :52
任務執行:獲取到結果 :4
任務執行:獲取到結果 :23
任務執行:獲取到結果 :16
線程池的任務全部完成:結果爲:367main線程關閉,進行線程的清理
使用時間:2017ms

好玩吧 時間延長了一半。

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