可以實作執行緒的複用,避免重新建立執行緒和銷毀執行緒。創建線程和銷毀線程對CPU的開銷是很大的。
可以限制最大可建立的執行緒數,可依照自己的機器效能動態調整執行緒池參數,提高應用程式效能。
提供定時執行、並發數控制等功能。
統一管理執行緒。
1:快取執行緒池(不建議)
2:固定容量執行緒池(不建議)
3:單一執行緒池(不建議)
4:定時任務執行緒池(不建議)
5:透過ThreadPoolExecutor建構方法建立執行緒池(阿里巴巴開發手冊十分推薦)
前面4種創建線程池的方式都是透過Executors的靜態方法來創建。
ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
為什麼不建議使用快取執行緒池?
原始碼分析
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
透過上面兩個程式碼片段,我們可以看出CachedThreadPool的maximumPoolSize為Integer的最大值2147483647,相當於可以無限的創建線程,而創建線程是需要內存的,這樣就會造成內存溢出,而且一般的機器也沒用那麼大的內存給它創建這麼大量的線程。
newFixedThreadPool(int num),num就是我們要指定的固定執行緒數量
ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
輸出:
pool-1-thread-575b3c38ff368dcafe01d2cfbd3d86ea2run>4
pool-1-thread-475b3c38ff368dcafe01d2cfbd3d86ea2run>3
pool-1-thread-575b3c38ff368dcafe01d2cfbd3d86ea2run> 5
pool-1-thread-375b3c38ff368dcafe01d2cfbd3d86ea2run>2
pool-1-thread-375b3c38ff368dcafe01d2cfbd3d86ea2run>8
pool-1-thread-3bbbb91469231aa81e311b16d706be062run>9
pool-1-thread-275b3c38ff368dcafe01d2cfbd3d86ea2run>1
pool-1-thread-175b3c38ff368dcafe01d2cfbd3d86ea2run>0
pool-1-thread- 575b3c38ff368dcafe01d2cfbd3d86ea2run>7
pool-1-thread-475b3c38ff368dcafe01d2cfbd3d86ea2run>6
可以看出起到了線程的複用。
為什麼FixedThreadPool是固定執行緒池?
原始碼分析
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
透過這個原始碼可以看出,核心執行緒數(corePoolSize)和最大執行緒數(maximumPoolSize)都為nThreads,因為只有這樣,執行緒池才不會進行擴容,線程數才固定。
ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
為什麼SingleThreadExecutor只含有一個執行緒?
原始碼分析
public static ExecutorService newSingleThreadExecutor() { return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
透過這個原始碼可以看出,核心執行緒數(corePoolSize)和最大執行緒數(maximumPoolSize)都為1,所以它只含有一個執行緒。
int initDelay=10; //初始化延时 int period=1;//初始化延迟过了之后,每秒的延时 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"); } },initDelay,period, TimeUnit.SECONDS);
這段程式碼的效果是:程式運作之後等10秒,然後輸出第一次結果,之後每隔1秒輸出一次結果。
為什麼不推薦使用ScheduledThreadPool?
原始碼分析
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, 2147483647, 10L, TimeUnit.MILLISECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue()); }
可以看出ScheduledThreadPool的最大線程數(maximumPoolSize)為Integer的最大值2147483647,相當於可以無限的創建線程,而創建線程是需要記憶體的,這樣就會造成記憶體溢出,而且一般的機器也沒用那麼大的記憶體給它創造這麼大量的線程。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < 12; i++) { final int finalI = i; threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { }
#corePoolSize:核心執行緒數。這些線程一旦被創建不會被銷毀,是一直存在的。線程池預設是沒有線程的,當有任務到來了,就會透過ThreadFactory去創建線程,並且一直存在。
maximumPoolSize:最大執行緒數。非核心執行緒數=maximumPoolSize-corePoolSize,非核心執行緒數其實就是可擴充的執行緒數,可能會被銷毀。
keepAliveTime:非核心執行緒的空閒存活時間。當透過擴容產生的非核心執行緒數在keepAliveTime這個時間後仍處於空閒狀態,則會銷毀這些非核心執行緒。
unit:keepAliveTime的時間單位,例如:秒
workQueue:等待區。當來了>corePoolSize的任務時會把任務存放在workQueue這個阻塞佇列中,等待其他執行緒處理。
threadFactory:線程工廠。創建線程的一種方式。
handler:拒絕策略。當來了>最大執行緒數 workQueue的容量則會執行拒絕策略
ArrayBlockingQueue:有界阻塞佇列。佇列有大小限制,當容量超過時則會觸發擴容或拒絕策略。
public ArrayBlockingQueue(int capacity) { this(capacity, false); }
LinkedBlockingQueue:無界阻塞佇列,佇列無大小限制,可能會造成記憶體溢位。
public LinkedBlockingQueue() { this(2147483647); }
AbortPolicy:直接拋例外
public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
DiscardPolicy:不作任何操作。默默丟棄任務
public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } }
DiscardOldestPolicy:丟掉存在時間最長的任務
public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }
CallerRunsPolicy:讓提交任務的執行緒去處理任務
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
threadFactory
ThreadFactory threadFactory = Executors.defaultThreadFactory(); threadFactory.newThread(new Runnable() { @Override public void run() { System.out.println("threadFactory"); } }).start();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < 26; i++) { //并发数26 final int finalI = i; threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); } /** * 核心线程数=10,最大线程数=20,故可扩容线程数=20-10 * BlockingQueue的大小为5,故等待区的大小为5,也就是当并发数<=核心线程数+5不会扩容,并发数大于16才会扩容 * * 触发扩容:并发数>核心线程数+阻塞队列的大小 * 对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容 * 因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略 */
为什么这段代码会触发拒绝策略
对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容,但是又因为因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略。
怎么触发扩容
触发扩容:并发数>核心线程数(corePoolSize)+阻塞队列(workQueue)的大小
使用Java纯手写一个线程池
以上是Java執行緒池如何創建的詳細內容。更多資訊請關注PHP中文網其他相關文章!