在Java開發中,多線程執行任務是很常見的,Java也提供了線程類Thread來讓我們方便創建一個線程如下代碼所示
new Thread(){
@Override
public void run() {
.....
}
}.start();
-
這樣創建新的線程有幾個缺點
- 每次要開啟新的線程都需要創建一個,性能差
- 線程隨意創建,缺乏統一的管理
- 不能做到線程的中斷
處理上面的這些問題,我們就需要使用線程池來管理線程。
Java SE5d的java.util.concurrent包 提供了Executor(執行器)來管理線程對象。Executor是一個接口,而ExecutorService繼承了Excutor接口,ExecutorService是一個具有生命周期的Executor,它知道如何構建恰當的上下文來執行Runnable對象。而ExecutorService對象是使用Executors的靜態方法得到Java中的線程池
//Executor接口實現
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
-
Java 的四種線程池
-Java中為我們提供了四種線程池,他們分別是FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingleThreadExector
-
FixedThreadPool
- 創建方式為使用Executors的newFixedThreadPool()方法來創建,這種線程池的線程數量是固定的,可以看到他的靜態方法需要傳入線程的數量,在空閑狀態下不會被系統回收,除非它被關閉了。當它的所有線程都在執行任務的時候,新加入的線程就會出來等待狀態,等到有線程空閑,新任務才會被執行,如果新任務加入時線程池中有空閑的線程,則意味著它可以快速響應處理任務。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } /* * 獲取使用方法 */ Runnable r=new Runnable(){ @Override run(){ ..... } } ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(r);
-
CachedThreadPool
創建方式為使用Executors的newCachedThreadPool()方法來創建,由其靜態方法可以看出,他的線程數是Integer.MAX_VALUE,可以說他的線程數是無限大,也就是說只要有任務,線程就會立即執行,但是它的每一個線程在空閑狀態下是有超時機制的,這個時間為60秒,只要線程空閑時間超過60秒該線程就會被回收,如果所有的線程都處于由空閑狀態并且超過了60秒,則相當于線程池中沒有任何,線程,也就是說這時的線程池是不占用任何資源的,所以這個線程池比較適合執行大量的耗時較少的任務
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } /* * 獲取使用方法 */ Runnable r=new Runnable(){ @Override run(){ ..... } } ExecutorService executor = Executors.newCachedThreadPool(); executor.execute(r);
-
ScheduledThreadPool
創建方式為使用Executors的newCachedThreadPool()方法來創建,這種線程池的核心線程數是固定的,而非核心線程數據是沒有限制的,并且當非核心線程空閑的的時候該線程就會被立即回收,所以我們可以使用他來操作定時任務和重復的任務(和Task TimeTask 有些像)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); } /* * 獲取使用方法 */ Runnable r=new Runnable(){ @Override run(){ ..... } } ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); // 1000ms 后執行任務 executor.schedule(r,1000,TimeUnit.MICROSECONDS); // 延遲1000ms 每隔1000ms 重復執行 任務 executor.scheduleAtFixedRate(r,1000,1000,TimeUnit.MICROSECONDS);
-
SingleThreadExector
- 創建方式為使用Executors的newCachedThreadPool()方法來創建,這種線程只有唯一一個核心線程,并且保證所有執行的的任務都在這一個線程中執行,并且是順序執行,也就不用在考慮線程同步的問題了。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } /* * 獲取使用方法 */ ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(r); Runnable r=new Runnable(){ @Override run(){ ..... } } ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(r);
-
-
通過上面對Java四種線程池的介紹,我們可以發現最終都是新建ThreadPoolExecutor對象,也就是說ThreadPoolExecutor才是線程池的核心實現。
-
ThreadPoolExecutor 比較常用的一個構造方法
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); } //參數含義 corePoolSize:默認情況下核心線程數會一直存在,不管是否處于閑置狀態, 但是如果線程池設置了核心線程數,也就是ThreadPoolExecutor的allowCoreThreadTimeOut這個boolean 為true,如果核心線程數為零,則allowCoreThreadTimeOut的值為true,超時時間為keepAliveTime的值, 也就是CachedThreadPool線程池的所有線程都能夠回收的原因,他的核心線程數為零,也就是沒有核心線程 maximumPoolSize:最大線程數 keepAliveTime:非核心線程超時時長,如果allowCoreThreadTimeOut的值為true, 則該超時時長也會作用于空閑狀態的核心線程 unit:超時時長的時間單位 TimeUnit.MILLISECONDS/SECONDS/MINUTES(毫秒/秒/分鐘) workQueue:線程任務隊列,存放線程任務 threadFactory:線程池線程生產工廠,為線程池創建新線程 //我們看到構造放中還有一個defaultHandler參數,他其實是RejectedExecutionHandler對象 defaultHandler:當線程隊列滿了,或者線程任務無法執行則用該參數拋出通知RejectedExecutionException,這里構造方法暫時沒用到
-
在Android中我們可以寫自己的線程管理類接,下面就來實現一個自己的線程池管理類來管理我們的線程
/** * Created by 毛麒添 on 2018/8/1 0010. * 線程管理類,線程池為單例 */ public class ThreadManager { private static ThreadPool mThreadPool; public static ThreadPool getmThreadPool(){ if (mThreadPool==null){ synchronized (ThreadManager.class){ if(mThreadPool==null){ //線程安全 mThreadPool=new ThreadPool(5,10,1L); } } } return mThreadPool; } //線程池 public static class ThreadPool{ private int corePoolSize;//核心線程數 5 private int maximumPoolSize;//最大線程數 10 private long keepAliveTime;//線程休眠時間 1秒 private ThreadPoolExecutor executor; private ThreadPool( int corePoolSize, int maximumPoolSize,long keepAliveTime){ this.corePoolSize=corePoolSize; this.maximumPoolSize=maximumPoolSize; this.keepAliveTime=keepAliveTime; } public void execute(Runnable runnable){ /** * int corePoolSize, 核心線程數 * int maximumPoolSize, 最大線程數 * long keepAliveTime, 線程休眠時間 * TimeUnit unit, 時間單位 * BlockingQueue<Runnable> workQueue, 線程隊列 * ThreadFactory threadFactory, 生成線程的工廠 */ if(executor==null){ executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); } //核心線程也有超時機制 executor.allowCoreThreadTimeOut(true); executor.execute(runnable); } //取消任務,從任務隊列中將其移除 public void cancelTask(Runnable runnable){ if(runnable!=null){ executor.getQueue().remove(runnable); } } } } //使用 ThreadManager.getmThreadPool().execute(new Runnable() { @Override public void run() { //執行任務 } });
-
好了,這就是我所了解的線程池知識,如果有錯,請給我留言指出,大家一起學習進步。
-
參考資料:
- 《Android開發藝術探索》
- 《Java編程思想》(第四版)
同步掘金地址