Java-線程池專題 (美團面試題)
去美團面試,問到了什麼是線程池,如何使用,為什麼要用,以下做個總結
1、什麼是線程池: java.util.concurrent.Executors提供了一個 java.util.concurrent.Executor介面的實現用於建立線程池
多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。
假設一個伺服器完成一項任務所需時間為: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而在處理請求時浪費時間,從而提高效率。
2.常見線程池 ①newSingleThreadExecutor 單個線程的線程池,即線程池中每次只有一個線程工作,單線程串列執行任務 ②newFixedThreadExecutor(n) 固定數量的線程池,沒提交一個任務就是一個線程,直到達到線程池的最大數量,然後後面進入等待隊列直到前面的任務完成才繼續執行 ③newCacheThreadExecutor(推薦使用) 可緩衝線程池, 當線程池大小超過了處理任務所需的線程,那麼就會回收部分空閑(一般是60秒無執行)的線程,當有任務來時,又智能的添加新線程來執行。 ④newScheduleThreadExecutor 大小無限制的線程池,支援定時和周期性的執行線程
java提供的線程池更加強大,相信理解線程池的工作原理,看類庫中的線程池就不會感到陌生了。
文章2:
Java線程池使用說明 一簡介
線程的使用在java中佔有極其重要的地位,在jdk1.4極其之前的jdk版本中,關於線程池的使用是極其簡陋的。在jdk1.5之後這一情況有了很大的改觀。Jdk1.5之後加入了java.util.concurrent包,這個包中主要介紹java中線程以及線程池的使用。為我們在開發中處理線程的問題提供了非常大的協助。 二:線程池
線程池的作用:
線程池作用就是限制系統中執行線程的數量。
根據系統的環境情況,可以自動或手動設定線程數量,達到啟動並執行最佳效果;少了浪費了系統資源,多了造成系統擁擠效率不高。用線程池控制線程數量,其他線程排隊等候。一個任務執行完畢,再從隊列的中取最前面的任務開始執行。若隊列中沒有等待進程,線程池的這一資源處於等待。當一個新任務需要運行時,如果線程池中有等待的背景工作執行緒,就可以開始運行了;否則進入等待隊列。
為什麼要用線程池:
1.減少了建立和銷毀線程的次數,每個背景工作執行緒都可以被重複利用,可執行多個任務。
2.可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因為消耗過多的記憶體,而把伺服器累趴下(每個線程需要大約1MB記憶體,線程開的越多,消耗的記憶體也就越大,最後死機)。
Java裡麵線程池的頂級介面是Executor,但是嚴格意義上講Executor並不是一個線程池,而只是一個執行線程的工具。真正的線程池介面是ExecutorService。
比較重要的幾個類:
| ExecutorService |
真正的線程池介面。 |
| ScheduledExecutorService |
能和Timer/TimerTask類似,解決那些需要任務重複執行的問題。 |
| ThreadPoolExecutor |
ExecutorService的預設實現。 |
| ScheduledThreadPoolExecutor |
繼承ThreadPoolExecutor的ScheduledExecutorService介面實現,週期性任務調度的類實現。 |
要配置一個線程池是比較複雜的,尤其是對於線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優的,因此在Executors類裡面提供了一些靜態工廠,產生一些常用的線程池。
1. newSingleThreadExecutor
建立一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當於單線程串列執行所有任務。如果這個唯一的線程因為異常結束,那麼會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。
2.newFixedThreadPool
建立固定大小的線程池。每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那麼線程池會補充一個新線程。
3. newCachedThreadPool
建立一個可快取的線程池。如果線程池的大小超過了處理任務所需要的線程,
那麼就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於作業系統(或者說JVM)能夠建立的最大線程大小。
4.newScheduledThreadPool
建立一個大小無限的線程池。此線程池支援定時以及周期性執行任務的需求。
執行個體
1:newSingleThreadExecutor
package com.thread;/* * 通過實現Runnable介面,實現多線程 * Runnable類是有run()方法的; * 但是沒有start方法 * 參考: * http://blog.csdn.net/qq_31753145/article/details/50899119 * */public class MyThread extends Thread { @Override public void run() { // TODO Auto-generated method stub// super.run(); System.out.println(Thread.currentThread().getName()+"正在執行...."); } }
package com.thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/* * 通過實現Runnable介面,實現多線程 * Runnable類是有run()方法的; * 但是沒有start方法 * 參考: * http://blog.csdn.net/qq_31753145/article/details/50899119 * */public class singleThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub //建立一個可重用固定線程數的線程池 ExecutorService pool=Executors.newSingleThreadExecutor(); //建立實現了Runnable介面對象,Thread對象當然也實現了Runnable介面; Thread t1=new MyThread(); Thread t2=new MyThread(); Thread t3=new MyThread(); Thread t4=new MyThread(); Thread t5=new MyThread(); //將線程放到池中執行; pool.execute(t1); pool.execute(t2); pool.execute(t3); pool.execute(t4); pool.execute(t5); //關閉線程池 pool.shutdown(); }}
結果:
pool-1-thread-1正在執行....pool-1-thread-1正在執行....pool-1-thread-1正在執行....pool-1-thread-1正在執行....pool-1-thread-1正在執行....
2newFixedThreadPool
package com.thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/* * 通過實現Runnable介面,實現多線程 * Runnable類是有run()方法的; * 但是沒有start方法 * 參考: * http://blog.csdn.net/qq_31753145/article/details/50899119 * */public class fixedThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub //建立一個可重用固定線程數的線程池 ExecutorService pool=Executors.newFixedThreadPool(2); //建立實現了Runnable介面對象,Thread對象當然也實現了Runnable介面; Thread t1=new MyThread(); Thread t2=new MyThread(); Thread t3=new MyThread(); Thread t4=new MyThread(); Thread t5=new MyThread(); //將線程放到池中執行; pool.execute(t1); pool.execute(t2); pool.execute(t3); pool.execute(t4); pool.execute(t5); //關閉線程池 pool.shutdown(); }}
結果:
pool-1-thread-1正在執行....pool-1-thread-1正在執行....pool-1-thread-1正在執行....pool-1-thread-1正在執行....pool-1-thread-2正在執行....
3、newCachedThreadPool
package com.thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/* * 通過實現Runnable介面,實現多線程 * Runnable類是有run()方法的; * 但是沒有start方法 * 參考: * http://blog.csdn.net/qq_31753145/article/details/50899119 * */public class cachedThreadExecutorTest{ public static void main(String[] args) {