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線程池的任務全部完成:結果為:621,main線程關閉,進行線程的清理使用時間:1004ms
我們試著把線程池的大小減少一半
任務執行:擷取到結果 :87任務執行:擷取到結果 :60任務執行:擷取到結果 :13任務執行:擷取到結果 :18任務執行:擷取到結果 :8任務執行:擷取到結果 :86任務執行:擷取到結果 :52任務執行:擷取到結果 :4任務執行:擷取到結果 :23任務執行:擷取到結果 :16線程池的任務全部完成:結果為:367,main線程關閉,進行線程的清理
使用時間:2017ms
轉:http://liuyieyer.iteye.com/blog/2083111
附: 線程池工程原理
多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。 假設一個伺服器完成一項任務所需時間為:T1 建立線程時間,T2 線上程中執行任務的時間,T3 銷毀線程時間。 如果:T1 + T3 遠大於 T2,則可以採用線程池,以提高伺服器效能。 一個線程池包括以下四個基本組成部分: 1、線程池管理器(ThreadPool):用於建立並管理線程池,包括 建立線程池,銷毀線程池,添加新任務; 2、背景工作執行緒(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,可以迴圈的執行任務; 3、任務介面(Task):每個任務必須實現的介面,以供背景工作執行緒調度任務的執行,它主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀態等; 4、任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩衝機制。 線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程式效能的。它把T1,T3分別安排在伺服器程式的啟動和結束的時間段或者一些閒置時間段,這樣在伺服器程式處理客戶請求時,不會有T1,T3的開銷了。 線程池不僅調整T1,T3產生的時間段,而且它還顯著減少了建立線程的數目,看一個例子: 假設一個伺服器一天要處理50000個請求,並且每個請求需要一個單獨的線程完成。線上程池中,線程數一般是固定的,所以產生線程總數不會超過線程池中線程的數目,而如果伺服器不利用線程池來處理這些請求則線程總數為50000。一般線程池大小是遠小於50000。所以利用線程池的伺服器程式不會為了建立50000而在處理請求時浪費時間,從而提高效率。
代碼實現中並沒有實現任務介面,而是把Runnable對象加入到線程池管理器(ThreadPool),然後剩下的事情就由線程池管理器(ThreadPool)來完成了