- 使用Executors工廠類創建線程池
1、使用newCachedThreadPool()方法創建無界線程池
newCachedThreadPool()方法創建的是無界線程池,可以進行線程自動回收,此類線程池中存放線程個數理論值為Integer.MAX_VALUE最大值。
package com.wjg.unit4_2_2; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Run { public static void main(String[] args) throws InterruptedException { Run run = new Run(); ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { executorService.execute(run.new MyRunnable(" "+(i+1))); } Thread.sleep(3000); System.out.println(); System.out.println(); for (int i = 0; i < 5; i++) { executorService.execute(run.new MyRunnable(" "+(i+1))); } } public class MyRunnable implements Runnable{ private String username; public MyRunnable(String username) { this.username = username; } @Override public void run() { System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis()); System.out.println(Thread.currentThread().getName()+" username="+username+" end "+System.currentTimeMillis()); } } }
執行結果:
pool-1-thread-1 username= 1 begin 1488268086641
pool-1-thread-3 username= 3 begin 1488268086641
pool-1-thread-2 username= 2 begin 1488268086641
pool-1-thread-2 username= 2 end 1488268086641
pool-1-thread-4 username= 4 begin 1488268086642
pool-1-thread-4 username= 4 end 1488268086642
pool-1-thread-3 username= 3 end 1488268086641
pool-1-thread-1 username= 1 end 1488268086641
pool-1-thread-5 username= 5 begin 1488268086642
pool-1-thread-5 username= 5 end 1488268086642
pool-1-thread-5 username= 1 begin 1488268089647
pool-1-thread-3 username= 3 begin 1488268089648
pool-1-thread-4 username= 4 begin 1488268089648
pool-1-thread-1 username= 2 begin 1488268089647
pool-1-thread-1 username= 2 end 1488268089648
pool-1-thread-4 username= 4 end 1488268089648
pool-1-thread-3 username= 3 end 1488268089648
pool-1-thread-2 username= 5 begin 1488268089648
pool-1-thread-2 username= 5 end 1488268089648
pool-1-thread-5 username= 1 end 1488268089648
通過線程的名字,可以看出來線程是從池中取出來的,是可以復用的。
2、使用newCachedThreadPool(ThreadFactory)定制線程工廠
構造函數ThreadFactory是實現定制Thread的作用,具體可以看下面的例子。
package com.wjg.unit4_2_3; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; public class Run { public static void main(String[] args) { Run run = new Run(); MyThreadFactory factory = run.new MyThreadFactory(); ExecutorService executorService = Executors.newCachedThreadPool(factory); executorService.execute(new Runnable() { @Override public void run() { System.out.println("當前線程的自定義名稱為:"+ Thread.currentThread().getName()); } }); } public class MyThreadFactory implements ThreadFactory{ @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName("自定義名稱:"+new Date()); return thread; } } }
執行結果:
當前線程的自定義名稱為:自定義名稱:Tue Feb 28 15:58:13 CST 2017
3、使用newFixedThreadPool(int) 方法創建有界線程池
此方法創建的是有界線程池,也就是池中的線程的個數可以指定最大值。
package com.wjg.unit4_2_4; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Run { public static void main(String[] args) { Run run = new Run(); ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) { executorService.execute(run.new MyRunnable(" "+(i+1))); } } public class MyRunnable implements Runnable{ private String username; public MyRunnable(String username) { this.username = username; } @Override public void run() { System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" username="+username+" end "+System.currentTimeMillis()); } } }
執行結果:
pool-1-thread-1 username= 1 begin 1488269132995
pool-1-thread-3 username= 3 begin 1488269132995
pool-1-thread-2 username= 2 begin 1488269132995
pool-1-thread-2 username= 2 end 1488269136000
pool-1-thread-3 username= 3 end 1488269136000
pool-1-thread-2 username= 4 begin 1488269136000
pool-1-thread-3 username= 5 begin 1488269136000
pool-1-thread-1 username= 1 end 1488269136000
pool-1-thread-2 username= 4 end 1488269139002
pool-1-thread-3 username= 5 end 1488269139002
通過執行結果可以看出,線程池中的線程最大數量為3。
4、使用newSingleThreadExecutor()方法創建單一線程池
此方法可以創建單一線程池,線程池里只有一個線程,單一線程池可以實現以隊列的方式來執行任務。
package com.wjg.unit4_2_5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Run { public static void main(String[] args) { Run run = new Run(); ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 5; i++) { executorService.execute(run.new MyRunnable(" "+(i+1))); } } public class MyRunnable implements Runnable{ private String username; public MyRunnable(String username) { this.username = username; } @Override public void run() { System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" username="+username+" end "+System.currentTimeMillis()); } } }
執行結果:
pool-1-thread-1 username= 1 begin 1488269392403
pool-1-thread-1 username= 1 end 1488269395409
pool-1-thread-1 username= 2 begin 1488269395409
pool-1-thread-1 username= 2 end 1488269398412
pool-1-thread-1 username= 3 begin 1488269398413
pool-1-thread-1 username= 3 end 1488269401418
pool-1-thread-1 username= 4 begin 1488269401418
pool-1-thread-1 username= 4 end 1488269404422
pool-1-thread-1 username= 5 begin 1488269404422
pool-1-thread-1 username= 5 end 1488269407423
由執行結果的線程名字可以看出,線程池中只有一個線程。
- ThreadPoolExecutor的使用
類ThreadPoolExecutor可以非常方便的創建線程池對象。
常用的構造方法有ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)
參數解釋如下:
corePoolSize:池中所保存的線程數,包括空閑線程數,也就是核心池的大小。
maximumPoolSize:池中允許的最大線程數
keepAliveTime:當線程數量大於corePoolSize值時,在沒有超過指定的時間內是不從線程池中將空閑線程刪除的,如果超過此時間,則刪除。
unit:keepAliveTime參數的時間單位。
workQueue:執行前用於保持任務的隊列。此隊列僅保存由execute方法提交的Runnable任務。
為了更好地理解這些參數在使用上的一些關系,可以將它們進行詳細化的注釋:
(1)A代表execute(runnable)欲執行的runnable的數量;
(2)B代表corePoolSize;
(3)C代表maximumPoolSize;
(4)D代表A-B(假設A>=B);
(5)E代表newLinkedBlockingDeque<Runnable>()隊列,無構造函數。
(6)F代表SynchronousQueue隊列;
(7)G代表keepAliveTime;
在使用線程池的過程下,會出現以下的集中情況:
(1)如果A<=B,那么馬上創建線程運行這個任務,並不放入擴展隊列Queue中,其他參數功能忽略;
(2)如果A>B&&A<=C&&E,則C和G參數忽略,並把D放入E中等待被執行;
(3)如果A>B&&A<=C&&F,則C和G參數有效,並且馬上創建線程運行這些任務,而不把D放入F中,D執行完任務后在指定時間后發生超時時將D進行清除。
(4)如果A>B&&A>C&&E,則C和G參數忽略,並把D放入E中等待被執行;
(5)如果A>B&&A>C&&F,則處理C的任務,其他任務則不再處理拋出異常;
方法getActiveCount()的作用是取得有多少個線程正在執行任務。
方法getPoolSize()的作用是獲得當前線程池里面有多少個線程,這些線程數包括正在執行任務的線程,也包括正在休眠的線程。
方法getCompletedTaskCount()的作用是取得有多少個線程已經執行完任務了。
方法getCorePoolSize()的作用是取得構造方法傳入的corePoolSize參數值。
方法getMaximumPoolSize()的作用是取得構造方法傳入的maximumPoolSize的值。
方法getTaskCount()的作用是取得有多少個任務發送給了線程池。
方法shutdown()的作用是使當前未執行完的線程繼續執行,而不再添加新的任務task,該方法不會阻塞,調用之后,主線程main馬上結束,而線程池會繼續運行直到所有任務執行完才會停止。
方法shutdownNow()的作用是中斷所有的任務task,並且拋出InterruptedException異常,前提是在Runnable中使用if(Thread.currentThread().isInterrupted()==true)語句來判斷當前線程的中斷狀態,而未執行的線程不再執行,也就是從執行隊列中清除。如果不手工加if語句及拋出異常,則池中正在運行的線程知道執行完畢,而未執行的線程不再執行,也從執行隊列中清除。
方法isShutDown()的作用是判斷線程池是否已經關閉。
方法isTerminating()的作用是判斷線程池是否正在關閉中。
方法isTerminated()的作用是判斷線程池是否已經關閉。
方法awaitTermination(long timeout,TimeUnit unit)的作用是查看在指定的時間之內,線程池是否已經終止工作,也就是最多等待多少時間后去判斷線程池是否已經終止工作。
方法allowsCoreThreadTimeOut(boolean) 的作用是配置核心線程是否有超時的效果。
方法prestartCoreThread()的作用是每調用一次就創建一個核心線程,返回值為boolean。
方法prestartAllCoreThreads()的作用是啟動全部核心線程,返回值是啟動核心線程的數量。
- 線程池ThreadPoolExecutor的拒絕策略
線程池中的資源全部被占用的時候,對新添加的Task任務有不同的處理策略,在默認的情況ThreadPoolExecutor類中有4個不同的處理方式:
(1)AbortPolicy:當任務添加到線程池中被拒絕時,它將拋出RejectedExecutionException異常。
(2)CallerRunsPolicy:當任務添加到線程池被拒絕時,會使用調用線程池的Thread線程對象處理被拒絕的任務。
(3)DiscardOldestPolicy:當任務添加到線程池被拒絕時,線程池會放棄等待隊列中最舊的未處理任務,然后將拒絕的任務添加到等待隊列中。
(4)DiscardPolicy:當任務添加到線程池中被拒絕時,線程池將丟棄被拒絕的任務。