1. 程式人生 > >Android 四種常見的執行緒池

Android 四種常見的執行緒池

引入執行緒池的好處

1)提升效能。建立和消耗物件費時費CPU資源

2)防止記憶體過度消耗。控制活動執行緒的數量,防止併發執行緒過多。

我們來看一下執行緒池的簡單的構造

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {...}
使用上面的方式建立執行緒池的話,我們需要配置一堆東西,非常麻煩,所以我們不建議這麼使用。而是推薦使用Executors的工廠方法來建立執行緒池,Executors類是官方提供的一個工廠類,它裡面封裝好了眾多功能不一樣的執行緒池。下面就介紹幾個常用的執行緒池。
public ThreadPoolExecutor(  
//核心執行緒數,除非allowCoreThreadTimeOut被設定為true,否則它閒著也不會死  
int corePoolSize,   
//最大執行緒數,活動執行緒數量超過它,後續任務就會排隊                     
int maximumPoolSize,   
//超時時長,作用於非核心執行緒(allowCoreThreadTimeOut被設定為true時也會同時作用於核心執行緒),閒置超時便被回收             
long keepAliveTime,                            
//列舉型別,設定keepAliveTime的單位,有TimeUnit.MILLISECONDS(ms)、TimeUnit. SECONDS(s)等  
TimeUnit unit,  
//緩衝任務佇列,執行緒池的execute方法會將Runnable物件儲存起來  
BlockingQueue<Runnable> workQueue,  
//執行緒工廠介面,只有一個new Thread(Runnable r)方法,可為執行緒池建立新執行緒  
ThreadFactory threadFactory)

1、FixedThreadPool() :

該方法返回一個固定執行緒數量的執行緒池,該執行緒池中的執行緒數量始終不變,即不會再建立新的執行緒,也不會銷燬已經建立好的執行緒,自始自終都是那幾個固定的執行緒在工作,所以該執行緒池可以控制執行緒的最大併發數。
栗子:假如有一個新任務提交時,執行緒池中如果有空閒的執行緒則立即使用空閒執行緒來處理任務,如果沒有,則會把這個新任務存在一個任務佇列中,一旦有執行緒空閒了,則按FIFO方式處理任務佇列中的任務。
public static ExecutorService newFixThreadPool(int nThreads){  
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
}  
//使用  
Executors.newFixThreadPool(5).execute(r);  

2、CachedThreadPool() :

該方法返回一個可以根據實際情況調整執行緒池中執行緒的數量的執行緒池。即該執行緒池中的執行緒數量不確定,是根據實際情況動態調整的。
栗子:假如該執行緒池中的所有執行緒都正在工作,而此時有新任務提交,那麼將會建立新的執行緒去處理該任務,而此時假如之前有一些執行緒完成了任務,現在又有新任務提交,那麼將不會建立新執行緒去處理,而是複用空閒的執行緒去處理新任務。那麼此時有人有疑問了,那這樣來說該執行緒池的執行緒豈不是會越集越多?其實並不會,因為執行緒池中的執行緒都有一個“保持活動時間”的引數,通過配置它,如果執行緒池中的空閒執行緒的空閒時間超過該“儲存活動時間”則立刻停止該執行緒,而該執行緒池預設的“保持活動時間”為60s。
public static ExecutorService newCachedThreadPool(int nThreads){  
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>());  
}  
//使用  
Executors.newCachedThreadPool().execute(r);  

3、SingleThreadExecutor() :

該方法返回一個只有一個執行緒的執行緒池,即每次只能執行一個執行緒任務,多餘的任務會儲存到一個任務佇列中,等待這一個執行緒空閒,當這個執行緒空閒了再按FIFO方式順序執行任務佇列中的任務。
public static ExecutorService newSingleThreadPool (int nThreads){  
    return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor (1, 1, 0, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()) );  
}  
//使用  
Executors.newSingleThreadPool ().execute(r); 


4、ScheduledThreadPool() :

該方法返回一個可以控制執行緒池內執行緒定時或週期性執行某任務的執行緒池。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){  
return new ScheduledThreadPoolExecutor(corePoolSize);  
}  
public ScheduledThreadPoolExecutor(int corePoolSize){  
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedQueue ());  
}  
//使用,延遲1秒執行,每隔2秒執行一次Runnable r  
Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 1000, 2000, TimeUnit.MILLISECONDS); 


自定義執行緒池

Android中常用的執行緒池就上面的四種,其實在Java中還有一種常見的執行緒池(newSingleThreadScheduledExecutor),其實上面的執行緒池對於我們開發已經是足夠了,不過有時候上面的仍然不能滿足我們,這時候我們就需要自定義不同功能的執行緒池。上面我們也說了執行緒池功能的不同歸根到底還是內部的BlockingQueue實現不同,所以,我們要實現我們自己相要的執行緒池,就必須從BlockingQueue的實現上做手腳。那麼我們接下來就用PriorityBlockingQueue來實現一個FIFO的執行緒池。

1)建立一個基於PriorityBlockingQueue的執行緒池

ExecutorService priorityThreadPool = new ThreadPoolExecutor(3,3,0L,TimeUnit.SECONDS,new PriorityBlockingQueue());

2)建立一個實現Runnable介面的類,並向外提供我們實現自定義功能,並實現Comparable介面

public abstract class PriorityRunnable implements Runnable, Comparable {
    private int priority;
 
    public PriorityRunnable(int priority) {
        if (priority 0)
            throw new IllegalArgumentException();
        this.priority = priority;
    }
 
    @Override
    public int compareTo(PriorityRunnable another) {
        int my = this.getPriority();
        int other = another.getPriority();
        return my 1 : my > other ? -1 : 0;
    }
 
    @Override
    public void run() {
        doSth();
    }
 
    public abstract void doSth();
 
    public int getPriority() {
        return priority;
    }
}

3)使用PriorityRunnable提交任務

ExecutorService priorityThreadPool = new ThreadPoolExecutor(3, 3, 0L, TimeUnit.SECONDS, new PriorityBlockingQueue());
        for (int i = 1; i 10; i++) {
            final int priority = i;
            priorityThreadPool.execute(new PriorityRunnable(priority) {
                @Override
                public void doSth() {
                    String threadName = Thread.currentThread().getName();
                    Log.v("zxy", "執行緒:" + threadName + ",正在執行優先順序為:" + priority + "的任務");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }