建立線程池首先須要定義本身的線程工廠類,至於爲何個人代碼的註釋裏有詳細解釋:java
/** *@description *@auth panmingshuai *@time 2018年3月27日下午11:54:27 * */ public class MyThreadFactory implements ThreadFactory { final AtomicInteger threadNumber = new AtomicInteger(1); final String namePrefix; MyThreadFactory(String namePrefix) { this.namePrefix = namePrefix+"-"; } public Thread newThread(Runnable r) { Thread thread = new Thread( r,namePrefix + threadNumber.getAndIncrement()); /** * java中線程分爲兩種類型:用戶線程和守護線程。經過Thread.setDaemon(false)設置爲用戶線程; * 經過Thread.setDaemon(true)設置爲守護線程。若是不設置,默認爲用戶線程。 * * * 用戶線程和守護線程的區別: * 1. 主線程結束後用戶線程還會繼續運行,JVM存活;主線程結束後守護線程和JVM的狀態又下面第2條肯定。 * 2.若是沒有用戶線程,都是守護線程,那麼JVM結束(隨之而來的是全部的一切煙消雲散,包括全部的守護線程)。 * 補充說明: * 定義:守護線程--也稱「服務線程」,在沒有用戶線程可服務時會自動離開。 * 優先級:守護線程的優先級比較低,用於爲系統中的其它對象和線程提供服務。 * 設置:經過setDaemon(true)來設置線程爲「守護線程」;將一個用戶線程設置爲守護線程的方式是在線程啓動用線程對象的setDaemon方法。 * example: 垃圾回收線程就是一個經典的守護線程,當咱們的程序中再也不有任何運行的Thread,程序就不會再產生垃圾, * 垃圾回收器也就無事可作,因此當垃圾回收線程是JVM上僅剩的線程時,垃圾回收線程會自動離開。它始終在低級別的狀態中運行, * 用於實時監控和管理系統中的可回收資源。 * 生命週期:守護進程(Daemon)是運行在後臺的一種特殊進程。它獨立於控制終端而且週期性地執行某種任務或等待處理某些發生的事件。 * 也就是說守護線程不依賴於終端,可是依賴於系統,與系統「同生共死」。那Java的守護線程是什麼樣子的呢。 * 當JVM中全部的線程都是守護線程的時候,JVM就能夠退出了;若是還有一個或以上的非守護線程則JVM不會退出。 * 對於一些後臺服務,更傾向於使用守護線程 */ if (thread.isDaemon()) thread.setDaemon(false); if (thread.getPriority() != Thread.NORM_PRIORITY) thread.setPriority(Thread.NORM_PRIORITY); return thread; } }
接着是線程池的使用數組
/** *@description *@auth panmingshuai *@time 2018年3月28日上午12:11:05 * */ public class ThreadTest { public static void main(String[] args) throws InterruptedException, ExecutionException { /** * ThreadPoolExecutor的構造方法: * public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime, * TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) * * corePoolSize:核心池的大小,這個參數跟後面講述的線程池的實現原理有很是大的關係。在建立了線程池後, * 默認狀況下,線程池中並無任何線程,而是等待有任務到來才建立線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法, * 從這2個方法的名字就能夠看出,是預建立線程的意思,即在沒有任務到來以前就建立corePoolSize個線程或者一個線程。 * 默認狀況下,在建立了線程池後,線程池中的線程數爲0,當有任務來以後,就會建立一個線程去執行任務,當線程池中的線程數目達到corePoolSize後, * 就會把到達的任務放到緩存隊列當中; * * maximumPoolSize(線程池最大大小):線程池容許建立的最大線程數。若是此時隊列也滿了,而且已建立的線程數小於最大線程數, * 則線程池會再建立新的線程執行任務。值得注意的是若是使用了無界的任務隊列這個參數就沒什麼效果。 * * keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。默認狀況下,只有當線程池中的線程數大於corePoolSize時, * keepAliveTime纔會起做用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時, * 若是一個線程空閒的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。 * 可是若是調用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數不大於corePoolSize時,keepAliveTime參數也會起做用, * 直到線程池中的線程數爲0; * * unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性: * TimeUnit.DAYS; //天 * TimeUnit.HOURS; //小時 * TimeUnit.MINUTES; //分鐘 * TimeUnit.SECONDS; //秒 * TimeUnit.MILLISECONDS; //毫秒 * TimeUnit.MICROSECONDS; //微妙 * TimeUnit.NANOSECONDS; //納秒 * * workQueue:用於保存等待執行的任務的阻塞隊列,即上面說的緩存隊列。能夠選擇如下幾個阻塞隊列。 * ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。 * LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量一般要高於ArrayBlockingQueue。 * 靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列。 * SynchronousQueue:一個不存儲元素的阻塞隊列。每一個插入操做必須等到另外一個線程調用移除操做,不然插入操做一直處於阻塞狀態, * 吞吐量一般要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。 * PriorityBlockingQueue:一個具備優先級得無限阻塞隊列。 * 通常狀況下使用LinkedBlockingQueue要多一些。 * * threadFactory:線程工廠,主要用來建立線程;最好用本身寫的,緣由是 * 一、爲了可以設置一個更有意義的線程名,若是咱們不本身定義,java就會調用本身的線程工廠DefaultThreadFactory, * 可是它建立的線程名字格式爲pool-m-thread-n, 也就是pool-1-thread-2,pool-2-thread-3, * 徹底看不出該線程爲什麼建立,在作什麼事情。在調試、監控和查看日誌時很是不便。 * 二、自主選擇線程類型:守護線程或用戶線程 * 三、能夠本身設置線程優先級 * * RejectedExecutionHandler(飽和策略):當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採起一種策略處理提交的新任務。 * 這個策略默認狀況下是AbortPolicy,表示沒法處理新任務時拋出異常。如下是JDK1.5提供的四種策略。 * 一、ThreadPoolExecutor.AbortPolicy:直接拋出異常。 * 二、ThreadPoolExecutor.CallerRunsPolicy:只用調用者所在線程來運行任務。 * 三、ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務。 * 四、ThreadPoolExecutor.DiscardPolicy:不處理,丟棄掉。 */ ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 2000, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(1000), new MyThreadFactory("pan"), new ThreadPoolExecutor.AbortPolicy()); //使用runnable // for(int i=0;i<1000;i++){ // executor.execute(new MyRunnable(i+"")); // } // executor.shutdown();//若是不調用這個方法,這裏面的線程可能不會執行完 //使用callable MyCallable callable = new MyCallable("pan"); FutureTask<String> task = new FutureTask<String>(callable); new Thread(task).start(); // 調用get()阻塞主線程,反之,線程不會阻塞 String result = task.get(); System.out.println(result); /** * runnable與callable的區別: * 相同點: * 一、二者均可用來編寫多線程程序; * 二、二者都須要調用Thread.start()啓動線程; * 不一樣點: * 二者最大的不一樣點是:實現Callable接口的任務線程能返回執行結果;而實現Runnable接口的任務線程不能返回結果; * callable接口的call()方法容許拋出異常;而Runnable接口的run()方法的異常只能在內部消化,不能繼續上拋; * Callable接口支持返回執行結果,此時須要調用FutureTask.get()方法實現,此方法會阻塞主線程直到獲取‘未來’結果; * 當不調用此方法時,主線程不會阻塞! * 由於callable能夠返回結果,當須要獲得返回結果是不能使用線程池,若是不須要返回結果時,可使用線程池, * 可使用FutureTask將callable封裝爲runnable。由於FutureTask實現了runnale接口因此能夠在線程池中使用 */ } }
須要說明的是callable和runnable均可以用線程調用執行它們的call方法或run方法,接着是我本身定義的實現這兩個接口的類緩存
/** *@description *@auth panmingshuai *@time 2018年3月28日上午12:04:45 * */ public class MyRunnable implements Runnable { private String message; public MyRunnable(String message) { this.message = message; } public void run() { try { Thread.sleep(1000); System.out.println("runable:" + message); } catch (InterruptedException e) { e.printStackTrace(); } } }
/** *@description *@auth panmingshuai *@time 2018年3月28日上午12:07:42 * */ public class MyCallable implements Callable<String> { private String message; public MyCallable(String message) { this.message = message; } public String call() throws Exception { System.out.println("callable:"+message); return message; } }
你能夠把你想經過線程池處理的任務放到這裏的run方法或者call方法中。多線程