編輯:關於Android編程
在操作系統中,線程是操作系統調度的最小單元,同事線程又是一種受限的系統資源,即線程不能無限制地產生,並且線程的創建和銷毀都有一定的開銷.當系統中存在大量的線程時,系統會通過時間輪轉片的方式調度每一個線程,因此線程不可能做到絕對的秉性,除非線程數量小於等於CPU的核心數,一般來說這是不可能的.試想一下,如果在一個線程中頻繁的創建和銷毀線程,這顯然不是高效的做法.正確的做法是——采用線程池.線程池中會緩存一定數量的線程,通過線程池就可以避免因為頻繁創建和銷毀線程所帶來的系統開銷.Android中的線程池來源於Java,主要是通過Excutor來派生特定類型的線程池,不同種類的線程池又具有各自的特性.
本篇博客基於java.util.concurrent包,記錄一下本人對線程池的理解,水平有限,難免出錯!
上圖是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); }
Executor接口是Executor框架中最基礎的部分了,定義了一個用於執行Runnable的execute()方法.它沒有直接的實現類,但是有一個重要的子接口ExecutorService.
//繼承自Executor接口 public interface ExecutorService extends Executor { /** * 關閉方法,調用後執行之前提交的任務,不再接受新的任務 */ void shutdown(); /** * 從語義上可以看出是立即停止的意思,將暫停所有等待處理的任務並返回這些任務的列表 */ ListExecutorService接口繼承自Executor接口,並對execute()方法進行了擴展,定義了終止任務,提交任務,跟蹤任務,返回結果等方法. ExecutorService類涉及到的Runnable接口,callable接口,Future接口的內容如下.shutdownNow(); /** * 判斷執行器是否已經關閉 */ boolean isShutdown(); /** * 關閉後所有任務是否都已完成 */ boolean isTerminated(); /** * 中斷 */ boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; /** * 提交一個Callable任務 */ Future submit(Callable task); /** * 提交一個Runable任務,result要返回的結果 */ Future submit(Runnable task, T result); /** * 提交一個任務 */ Future submit(Runnable task); /** * 執行所有給定的任務,當所有任務完成,返回保持任務狀態和結果的Future列表 */ List > invokeAll(Collection> tasks) throws InterruptedException; /** * 執行給定的任務,當所有任務完成或超時期滿時(無論哪個首先發生),返回保持任務狀態和結果的 Future 列表。 */ List > invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException; /** * 執行給定的任務,如果某個任務已成功完成(也就是未拋出異常),則返回其結果。 */ T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException; /** * 執行給定的任務,如果在給定的超時期滿前某個任務已成功完成(也就是未拋出異常),則返回其結果。 */ T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
public interface Runnable { /** * Starts executing the active part of the class' code. This method is * called when a thread is started that has been created with a class which * implements {@code Runnable}. */ public void run(); }
public interface Callable{ /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
// Future代表異步任務的執行結果 public interface Future{ /** * 嘗試取消一個任務,如果這個任務不能被取消(通常是因為已經執行完了),返回false,否則返回true。 */ boolean cancel(boolean mayInterruptIfRunning); /** * 返回代表的任務是否在完成之前被取消了 */ boolean isCancelled(); /** * 如果任務已經完成,返回true */ boolean isDone(); /** * 獲取異步任務的執行結果(如果任務沒執行完將等待) */ V get() throws InterruptedException, ExecutionException; /** * 獲取異步任務的執行結果(有最常等待時間的限制) * * timeout表示等待的時間,unit是它時間單位 */ V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
此類的結構如下
java.util.concurrent 類 ThreadPoolExecutor java.lang.Object 繼承者 java.util.concurrent.AbstractExecutorService 繼承者 java.util.concurrent.ThreadPoolExecutor
在ThreadPoolExecutor和ExecutorService接口之間還有一個實現接口的抽象類AbstractExecutorService,這裡就不做過多記錄,詳情可以參考API文檔
ThreadPoolExecutor是線程池真正的實現類,他的構造方法實現了一系列參數來配置線程池,下面記錄ThreadPoolExecutor的構造方法中每一個參數的含義,這些參數將會直接影響到線程池的功能特性.
下面是ThreadPoolExecutor的常用構造方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BolckingQueuecorePoolSizeworkQueue, ThreadFactory threadFactory)
線程的核心線程數,默認情況下,核心線程會在線程池中一直存活,及時他們處於閒置狀態.如果將ThreadPoolExecutor的allowCoreThreadTimeout屬性設置為true,那麼閒置的核心線程在等待新任務到來的時候會有超時策略,這個時間由keepAliveTime所指定,等待時間超過其所指定的時間後,核心線程就會被終止.
maximumPoolSize線程池所能容納的最大線程數,當活動線程數達到這個數值後,後續的新任務將會被阻塞.
keepAliveTime非核心線程閒置時的超時時長,超過這個時長,非核心線程就會被回收.當ThreadPoolExecutor的allowCoreThreadTimeout屬性設置為true時,keepAliveTimeout將同樣會作用於核心線程.
unit用於指定keepAliveTimeout參數的單位時間,這是一個枚舉,常用的有TimeUnit.MILLISECONDS,TimeUnit.SECONDS,TimeUnit.MINUTES.
workQueue線程池中的任務隊列,通過線程池的execute()方法提交的Runnable對象會存儲在這個參數中.
此處工作隊列常用的子類有三種,分別是:
ArrayBlockingQueue:
一個由數組支持的有界阻塞隊列。此隊列按 FIFO(先進先出)原則對元素進行排序。隊列的頭部 是在隊列中存在時間最長的元素。隊列的尾部 是在隊列中存在時間最短的元素。新元素插入到隊列的尾部,隊列獲取操作則是從隊列頭部開始獲得元素。
這是一個典型的“有界緩存區”,固定大小的數組在其中保持生產者插入的元素和使用者提取的元素。一旦創建了這樣的緩存區,就不能再增加其容量。試圖向已滿隊列中放入元素會導致操作受阻塞;試圖從空隊列中提取元素將導致類似阻塞。
LinckedBlockingQueue:
一個基於已鏈接節點的、范圍任意的 blocking queue。此隊列按 FIFO(先進先出)排序元素。隊列的頭部 是在隊列中時間最長的元素。隊列的尾部 是在隊列中時間最短的元素。新元素插入到隊列的尾部,並且隊列獲取操作會獲得位於隊列頭部的元素。鏈接隊列的吞吐量通常要高於基於數組的隊列,但是在大多數並發應用程序中,其可預知的性能要低。
可選的容量范圍構造方法參數作為防止隊列過度擴展的一種方法。如果未指定容量,則它等於 Integer.MAX_VALUE。除非插入節點會使隊列超出容量,否則每次插入後會動態地創建鏈接節點。
SynchronousQueue:
一種阻塞隊列,其中每個插入操作必須等待另一個線程的對應移除操作 ,反之亦然。同步隊列沒有任何內部容量,甚至連一個隊列的容量都沒有。不能在同步隊列上進行 peek,因為僅在試圖要移除元素時,該元素才存在;除非另一個線程試圖移除某個元素,否則也不能(使用任何方法)插入元素;也不能迭代隊列,因為其中沒有元素可用於迭代。隊列的頭 是嘗試添加到隊列中的首個已排隊插入線程的元素;如果沒有這樣的已排隊線程,則沒有可用於移除的元素並且 poll() 將會返回 null。對於其他 Collection 方法(例如 contains),SynchronousQueue 作為一個空 collection。此隊列不允許 null 元素。
這三種隊列對應的排隊策略分別是:有界隊列,無界隊列,直接提交.
線程工廠,為線程池提供創建新線程的功能.ThreadFactory是一個接口,他只有一個方法:Thread newThread(Runnable r).
以上ThreadPoolExecutor的參數配置在AsyncTask中有明顯的體現:
public abstract class AsyncTask{ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue (128); /** * An {@link Executor} that can be used to execute tasks in parallel. */ public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
從上面的代碼我們可以知道,AsyncTask對THREAD_POOL_EXECUTOR這個線程池進行了配置,配置後的線程池規格如下:
核心線程數等於CPU核心數+1; 線程池的最大線程數等於CPU核心數的2倍+1; 核心線程無超時機制,非核心線程在閒置時的超時時間為1秒; 任務隊列采用的是容量為128的有界隊列; -Executors中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。此類支持以下各種方法:
創建並返回設置有常用配置字符串的 ExecutorService 的方法。 創建並返回設置有常用配置字符串的 ScheduledExecutorService 的方法。 創建並返回“包裝的”ExecutorService 方法,它通過使特定於實- - 現的方法不可訪問來禁用重新配置。 創建並返回 ThreadFactory 的方法,它可將新創建的線程設置為已知的狀態。創建並返回非閉包形式的 Callable 的方法,這樣可將其用於需要 Callable 的執行方法中。
Executors提供的都是工具形式的方法,所以都是static的,並且這個類也沒有必要實例化,所以它的構造方法時private的。
下面結合線程池的分類,來記錄Executors類的幾個常用創建線程池的靜態方法.
FixedThreadPoolpublic static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
CachedThreadPool
通過Executors的newCachedThreadPool方法來創建.它是一種線程數量不定的線程池,且只有非核心線程.並且最大線程數為Integer.MAX_VALUE,相當於任意大. 當線程池中的線程都處於活動狀態時,線程池會創建新的線程來處理新任務,否則就會利用空閒的線程來處理新任務. 線程池中的空閒線程都有超時機制,這個超時時長是60s,超過60s閒置線程就會被收回. 和FixedThreadPool不同的是,CacheTHreadPool的任務隊列相當於一個空的集合,這將導致任何任務都會立即被執行,因為在這種場合下SynchronousQueue是無法插入任務的. 當整個線程池都處於閒置狀態時,線程池中的線程都會超時而被停止,這個時候CacheThreadPool之中實際上是沒有任何線程的,它幾乎不占任何系統資源. 這類線程池比較適合執行大量的耗時較少的任務.public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
ScheduledThreadPool
通過Executors的newScheduledThreadPool方法來創建. 它的核心線程數是固定的,非核心線程數是沒有限制的,並且當非核心線程閒置時,會立即被回收. 這類線程池主要用於執行定時任務和具有固定周期的重復任務.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()); }SingleThreadPool
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
以上就是Android中常見的四種線程池,出了上面系統提供的四種線程以外,我們還可以根據實際的需要,靈活的配置線程池.
重用線程池中的線程,避免因為線程的創建和銷毀帶來的系統性能開銷.
能有效地控制線程池最大並發數,避免大量的線程之間因為相互搶占資源而導致的阻塞現象.
能夠對線程進行簡單的管理,並提供定時執行以及指定間隔循環執行等功能.
如果線程池中的線程數量未達到核心線程數,那麼會直接啟動一個核心線程來執行任務.
如果線程池中的線程數量已經達到或超過核心線程數,那麼任務會被插入到任務隊列中排隊等待執行.
如果在步驟2中無法插入到任務隊列中,這往往是由於隊列已滿,這個時候如果線程數未達到線程池規定的最大值,那麼會立刻啟動一個非核心線程類執行任務
如果步驟3中線程數已達到線程池規定的最大值,那麼就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者.
下一篇結合Callable,Future,FutureTask記錄下線程池的具體使用.
Batterystats & Battery Historian Walkthrough Battery Historian Charts Android應用的
在使用android-async-http的時候我的apl 更新到了23,我的build version也是23的時候出現了,org.apache.http.Heade
效果圖如下所示:先給大家說下實現android 跳轉到通訊錄的實現思路:1.點擊跳轉到通訊錄界面2.獲取通訊錄姓名和手機號碼3.回調顯示姓名和手機號碼1首先是跳轉到通訊錄
在前不久的谷歌2015 I/O大會上,發布了Android新版本M,貌似從這個版本開始Android不在以數字命名版本了。在這次的I/O大會上谷歌對Andro