編輯:關於Android編程
最近在項目中接觸到了很多有關於多線程方面的東西,並且剛好前段時間看了Java並發編程實戰那本說,
所以想將自己所了解到的,以及實際中碰到的問題總結一下。
打算首先介紹一下,Java多線程相關的基礎,例如Thread,Runnable。雖然這個極其的基礎,但是我覺得任何東西都
繞不過基礎的知識。重點會在介紹線程池,包括線程池相關類的層級結構、相關參數等。
以及在Android中有那些多線程表現形式,例如:AsyncTask,HandlerThread,IntentService, AsycTaskLoader等。
之後希望能簡單的表述一下多線程與Future, TimeUnit , CountDownLatch 等等相關類的關系,以及如何在子線程中如何開啟子線程去請求數據,子線程與主線程如何交互數據等。
還有就是介紹多線程相關概念,鎖, 可重入鎖, 死鎖, 如何排查死鎖, 線程發布, 競態條件, 數據競爭 等等
應該會分幾篇博客來總結相關的知識。當然其中肯定會有一些錯誤之處,歡迎留言指出。我會及時更正。
以上相關所有知識點,有許多借鑒自其他的大神的博客,由於看了許多,沒辦法一一列舉,在此表示感謝。許多概念相關的知識來自於《Java並發編程實戰》那本書。
一、Thread 繼承Thread類,重寫run方法。
我覺得Thread應該是被最經常用到的。當只需要一個簡單的線程,不需要管理線程時直接:
new Thread(){ @Override public void run() { //請求網絡等 } }.start();
一個匿名內部類就寫完了,相信很多人都寫過這樣的代碼。需要注意的是,如果在Activity中寫這樣的代碼很容易造成內存洩露。
即當Activity結束時,線程的工作還沒有做完,這就會導致該Activity不會被回收。可以考慮用靜態內部類的形式代替上面的寫法。
二、 Runnable 實現Runnable接口重寫run()方法
public class DisableRunnable implements Runnable{ @Override public void run() { //請求網絡等 } }
Runnable 是執行任務的單元,需要用Thread包裝一下才可以執行。
Runnable 接口在Android中的使用也非常的多.例如View.post(),Handler.post()等等。
每當我們看到這樣的代碼時:
new Thread(){runnable}.start();
當你考慮用更靈活的策略來執行任務時,可以考慮利用線程池代替Thread。
三、 線程池:
下面主要說一下線程池相關的總結,
線程池結構:
最頂層接口 Executor
public interface Executor { void execute(Runnable command); }
ExecutorService接口 繼承Executor 定義了一些有關於生命周期的方法。
public interface ExecutorService extends Executor { void shutdown(); ListshutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; Future submit(Callable task); Future submit(Runnable task, T result); Future submit(Runnable task); List > invokeAll(Collection> tasks) throws InterruptedException; List > invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException; T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
ThreadPoolExecutor 間接實現了ExecutorService 是真正做事的線程池。最常見的幾種線程池都是它的實現。
ThreadPoolExecutor 常用的構造方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, RejectedExecutionHandler handler){}
相關參數解釋:
corePoolSize 核心線程池大小
maximumPoolSize 最大線程池大小
keepAliveTime 線程池中超過corePoolSize數目的空閒線程最大存活時間;可以allowCoreThreadTimeOut(true)使得核心線程有效時間
TimeUnit keepAliveTime時間單位
workQueue 阻塞任務隊列
RejectedExecutionHandler 飽和策略 當提交任務數超過maxmumPoolSize+workQueue之和時,任務會交給RejectedExecutionHandler來處理
1.當線程池小於corePoolSize時,新提交任務將創建一個新線程執行任務,即使此時線程池中存在空閒線程。
2.當線程池達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行
3.當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會創建新線程執行任務
4.當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理
5.當線程池中超過corePoolSize線程,空閒時間達到keepAliveTime時,關閉空閒線程
注意:
這裡maximumPoolSize 指的是corePoolSize + 由於隊列已滿並且maximumPoolSize>corePoolSize時,為執行任務創建的線程數。
所以第4條應該說成,當workQueue已滿,並且線程池內的線程數已經達到了maximumPoolSize最大線程數。這時,再次提交任務會交給RejectedExecutionHandler 飽和策略去處理。
四、這裡BlockingQueue
可以為:SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue 等等。
我們需要先了解這幾個隊列,當清楚這些隊列的性質後,再去看那幾個常用的線程池,將會非常容易理解。
SynchronousQueue
SynchronousQueue 首先它是無界的,也就是說它存儲的能力是沒有限制的。
但是它每一個put操作必須等待一個take操作。也就是說在某一次添加元素後,必須要等其他線程將它取走,否則不能添加。
LinkedBlockingQueue
LinkedBlockingQueue 無界隊列,也就是說使用LinkedBlockingQueue時,當所有corePoolSize都在忙時,新任務都會在LinkedBlockingQueue中等待。
因為LinkedBlockingQueue是無界的(最大長度為Integer.MAX_VALUE,這裡認為無界)。這個時候maximumPoolSize其實就無效了,因為只有當LinkedBlockingQueue已滿時,才會創建新的線程執行任務。而這裡LinkedBlockingQueue永遠都不會滿。所以maximumPoolSize也就無效了,也就是說利用這個隊列構造的線程池,永遠也不會創建新的線程。
ArrayBlockingQueue
ArrayBlockingQueue 有界隊列 可以設置隊列的大小,能夠有效的防止資源消耗。但是者也就導致這種情況控制起來會相對復雜,JDK建議的幾種常見的線程池都沒有使用這個隊列。
PriorityBlockingQueue 優先級隊列
Executors 線程池工具類,用於生產線程池。
五、幾類比較常見的線程池:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
解釋:構建一個固定數目的線程池。corePoolSize 與 maximumPoolSize 數值相等。當線程池中沒有空閒線程時,利用 LinkedBlockingQueue
保存阻塞任務,因為之前已經說過了 LinkedBlockingQueue 是無界的,也就是說這個隊列永遠不會滿,那maximumPoolSize 其實就沒有任何意義。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
解釋:構建一個單線程(只有一個線程,單不一定是同一個線程)的線程池。corePoolSize 與 maximumPoolSize 均為1 .利用無界的LinkedBlockingQueue 作為保存任務的隊列,這樣就保證無論提交多少個人物,線程池只能有一個線程在運行,其余的任務均保存在 LinkedBlockingQueue 隊列中。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
解釋:構建一個具有緩存功能的線程池,corePoolSize = 0, maximumPoolSize = Integer.MAX_VALUE 。隊列用的是SynchronousQueue。每加入一個任務,線程池便會構建一個線程將它取出執行(如果有空閒的線程,會利用空閒的線程,而不會創建新的線程),這樣,加入多少個任務便會創建相同個數的線程。所以這個線程池的 maximumPoolSize 為Integer.MAX_VALUE。這就保證了線程池的最大線程數是無界的,理論上線程池可以有任意個線程。當線程執行完任務後,超過60秒的空閒時間即被回收銷毀。
六、飽和策略:
RejectedExecutionHandler 參數用來表示飽和策略。即表示當有界隊列已滿,並且當前線程池中的線程數已達到 maximumPoolSize ,這時再提交任務,會交給RejectedExecutionHandler 來處理。
注意:這裡一定是有界隊列,因為無界隊列我們認為是永遠也無法填滿的(SynchronousQueue直接由生產者提交給工作線程),那麼也就永不到飽和策略了。
public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ThreadPoolExecutor executor); }
RejectedExecutionHandler 本身是一個接口。在Executors中提供了幾個實現類。
1)AbortPolicy:中止,executor拋出未檢查RejectedExecutionException,調用者捕獲這個異常,然後自己編寫能滿足自己需求的處理代碼。
2)DiscardRunsPolicy:遺棄最舊的,選擇丟棄的任務,是本應接下來就執行的任務。
3)DiscardPolicy:遺棄會默認放棄最新提交的任務(這個任務不能進入隊列等待執行時)
4)CallerRunsPolicy:調用者運行,既不會丟棄哪個任務,也不會拋出任何異常,把一些任務推回到調用者那裡,以此減緩新任務流。它不會在池線程中執行最新提交的任務,但它會在一個調用了execute的線程中執行。
七、線程工廠:
線程池另外還有一個參數便是:ThreadFactory。之前在介紹常用的構造方法沒有說它的原因是,一般用不到。其他構造方法中都給出了默認實現:DefaultThreadFactory。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {} public interface ThreadFactory { Thread newThread(Runnable r); }
ThreadFactory 在 Executors 中也提供了幾個實現類,一般博主所接觸過的都是 DefaultThreadFactory 。當然我們可以自己定制,
用於添加一些Log等等。之前推薦的幾種線程池都有 ThreadFactory 作為參數的方法。
八、常見的線程池的使用:
自定義線程池:
private final static ExecutorService executorService = new ThreadPoolExecutor( 5, 10, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue(), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { //DO nothing } });
這裡我們用的是自定義的線程池。我們定義了一個核心線程池為5的線程池,線程池內最大的線程池數為10。
當然由於我們這裡定義的緩存隊列為LinkedBlockingQueue, 沒有制定隊列大小,那麼其默認無Integer.MAX_VALUE。
也就是無限大,所以最大線程數沒有用處。
飽和策略也是自定的,這裡當達到執行飽和策略時,什麼都不做,直接丟棄。
public void request() { executorService.execute(new Runnable() { @Override public void run() { try { //請求網絡等。 } catch (Exception e) { e.printStackTrace(); } } }); } 利用Executors 提供的線程池: public void request() { ExecutorService executor = Executors.newFixedThreadPool(5); executor.execute(new Runnable() { @Override public void run() { try { //請求網絡等。 } catch (Exception e) { e.printStackTrace(); } } }); }
OK, 線程池相關就介紹到這裡,下一篇會介紹Future,FutureTask, Callable 等等相關知識。
1 RecycleView實現ListView的功能相關方法:RecyclerView的方法: 方法 含義 setLayoutManager(&hellip
在 Android 上使用 RxNettyNetty是由JBOSS提供的一個Java開源框架,是一個支持TCP/UDP/HTTP等網絡協議的通信框架,和Mina類似,廣泛
Android快速入門 1. 搭建開發環境>解壓壓縮文件,得到:①Android SDK (類似於JDK)② Eclipse ③ADT>配置兩個pat
一、概述最近項目准備嘗試使用webp來縮小包的體積,於是抽空對相關知識進行了調研和學習。至於什麼是webp,使用webp有什麼好處我就不贅述了,具體可以參考騰訊isux上