線程相關分享
一 線程簡介:
1.什麼是線程:
1. 線程是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位,同一進程中可以存在多個線程併發執行。對於同一進程中的不同線程將共享該進程的所有系統資源,如虛擬地址,文件操作符,信號處理等等。但多個線程會有各自的調度棧,自己的寄存器環境等。
2.線程的狀態介紹:
-
NEW(創建狀態): 當線程被實例化創建
-
RUNNABLE(就緒及可運行狀態): 當實例化對象調用start()方法後線程處於就緒狀態,當cpu輪轉獲得執行後,線程就爲運行狀態.
-
BLOCKED (阻塞狀態):
(1). 調用了wait()方法,jvm會把該線程放到等待池中(又爲WAITING狀態)。
(2). 同步阻塞,該線程需要獲取同步鎖但是同步鎖此時被別的線程佔用。jvm會將這個線程放置在鎖池中。
(3). 運行sleep()或者join()方法
-
TERMINATED (死亡狀態):執行完畢
3.線程中sleep()與wait()方法的區別:
- 方法來源不同,wait()方法來自Object類,wait()方法來自Thread類
- 執行中是否釋放同步鎖,wait()方法在執行過程中會釋放同步鎖,而sleep()在執行過程中不會釋放同步鎖。切執行sleep()的線程是可以主動打斷的。
- 方法執行過程不同,wait()必須在同步控制的方法內調用,而sleep()可以在任何地方使用.
- sleep()必須捕獲異常。
線程安全簡介:
線程安全是指多線程訪問同一代碼,避免產生不確定的結果。編寫線程安全的代碼主要是依靠線程間同步
1.線程安全:
-
常見的線程安全類:
Vector是線程安全的,ArrayList是線程不安全的
StringBuffer是線程安全的,StringBuilder是線程不安全的
Hashtable是線程安全的,HashMap是線程不安全的
2.併發與並行的區別:
- 並行就是兩個任務同時運行(需要多核CPU)
- 併發是指兩個任務都在請求運行,而處理器只能接受一個任務,就把這兩個任務安排輪轉執行,由於時間間隔短,給人感覺就是兩個任務同時在運行。
Android中的線程池簡介:
1.使用線程池的好處:
-
避免碎片化創建線程和銷燬線程,線程池中會有已創建線程,這樣可以避免線程的創建及銷燬對系統帶來的性能消耗。
-
線程池中可以控制線程最大併發數,避免線程間出現搶奪系統資源而導致阻塞的現象。
-
線程池可以對線程進行一個整體管理,包括線程定製執行,循環執行等。
-
在Alibaba的代碼規範中強烈推薦使用線程池來對程序內部的線程做出統一化管理,而建議不要使用 new Thread()的方法來隨手創建線程。
Inspection info: 線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。 說明:使用線程池的好處是減少在創建和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。如果不使用線程池,有可能造成系統創建大量同類線程而導致消耗完內存或者“過度切換”的問題。 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() .setNameFormat("demo-pool-%d").build(); ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); singleThreadPool.execute(()-> System.out.println(Thread.currentThread().getName())); singleThreadPool.shutdown();
2.實例化線程池中各個參數的含義
ThreadPoolExecutor的多參構造函數:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
hreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize(核心線程數):
線程池中核心線程數其中也包含空閒線程數,空閒線程數會有一個空閒時間間隔(keepAliveTime),當空閒時間超過這個時間間隔則會終止該線程。
maximumPoolSize(最大線程數):
線程池最大容納數,當活動的線程達到這個最大任務數後,後續新任務添加進來會被阻塞(可以通過設置參數來設置阻塞時的不同處理策略)
keepAliveTime(非核心線程超時時長):
非核心線程閒置超時時長,什麼是核心線程,非核心線程的含義可以用公式標明:maximunm - corePoolSize(既 最大容納數 - 核心線程數). 非核心線程就類比一個臨時工,當核心成員幹不完任務就會叫上臨時工一起,但是任務完成了臨時工什麼時候辭退就是自己指定了,回到文章,當超過這個時長時,非核心線程就會被回收,但是剛纔介紹corePoolSize的時候也說了,如果把allowCoreThreadTimeOut的屬性設置爲true,keepAliveTime也可以同樣對核心線程進行終止操作。
unit:
時間單位
workQueue(任務隊列):
線程池中的任務隊列(阻塞隊列),線程池的execute方法提交的Runnable對象存儲在這個隊列裏面,BlockingQueue是線程安全的,常用的阻塞隊列有如下五個 :
(1).LinkedBlockingQueue(鏈表阻塞隊列)
鏈表阻塞隊列,這個隊列由一個鏈表構成,它內部維護了一個數據緩存隊列,這個隊列按 FIFO(先進先出)排序元素。隊列的頭部 是在隊列中時間最長的元素。隊列的尾部 是在隊列中時間最短的元素。新元素插入到隊列的尾部,並且隊列獲取操作會獲得位於隊列頭部的元素。鏈接隊列的吞吐量通常要高於基於數組的隊列,但是在大多數併發應用程序中,其可預知的性能要低。 當構造這個隊列對象時,如果不指定隊列容量大小,它的默認容量大小是Integer.MAX_VALUE(無限大),這樣就是一個無界隊列,除非插入節點會使隊列超出容量,否則每次插入後會動態地創建鏈接節點。
(2).ArrayBlockingQueue(數組阻塞隊列)
數組阻塞隊列,這是一個有界阻塞隊列內部,維護了一個定長數組用來緩存隊列中的數據對象,通過兩個整型變量來標識隊列的頭跟尾在數組中的位置,這個隊列按 FIFO(先進先出)原則對元素進行排序。隊列的頭部是在隊列中存在時間最長的元素。隊列的尾部 是在隊列中存在時間最短的元素。新元素插入到隊列的尾部,隊列獲取操作則是從隊列頭部開始獲得元素。這是一個典型的“有界緩存區”,固定大小的數組在其中保持生產者插入的元素和使用者提取的元素。一旦創建了這樣的緩存區,就不能再增加其容量。試圖向已滿隊列中放入元素會導致操作受阻塞;試圖從空隊列中提取元素將導致類似阻塞。
此類支持對等待的生產者線程和使用者線程進行排序的可選公平策略。默認情況下,不保證是這種排序。然而,通過將公平性 (fairness) 設置爲 true 而構造的隊列允許按照 FIFO 順序訪問線程。公平性通常會降低吞吐量,但也減少了可變性和避免了“不平衡性”。
(3).DelayQueue(延遲隊列)
延遲隊列,它也是一個無界阻塞隊列,只有在延遲期滿時才能從中提取元素。這個隊列的頭部是延遲期滿後保存時間最長的 Delayed 元素。如果延遲都還沒有期滿,則隊列沒有頭部,並且 poll 將返回 null。當一個元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一個小於等於 0 的值時,將發生到期。即使無法使用 take 或 poll 移除未到期的元素,也不會將這些元素作爲正常元素對待。例如,size 方法同時返回到期和未到期元素的計數。此隊列不允許使用 null 元素。 通常用這個隊列來管理一個超時未響應隊列。
(4).PriorityBlockingQueue(優先阻塞隊列)
優先阻塞隊列,它也是一個無界阻塞隊列,它使用與類 PriorityQueue 相同的順序規則,並且提供了阻塞獲取操作。雖然這個隊列邏輯上是無界的,但是資源被耗盡時試圖執行 add 操作也將失敗(導致 OutOfMemoryError)。此類不允許使用null(空)元素。依賴自然順序的優先級隊列也不允許插入不可比較的對象(這樣做會導致拋出 ClassCastException)。 在創建隊列對象時通過構造函數中的Comparator屬性對象來決定優先級。
(5).SynchronousQueue(同步阻塞隊列)
同步阻塞隊列,一種無緩衝阻塞隊列,其中每個插入操作必須等待另一個線程的對應移除操作 ,反之亦然。同步隊列沒有任何內部容量,甚至連一個隊列的容量都沒有。不能在同步隊列上進行 peek,因爲僅在試圖要移除元素時,該元素才存在;除非另一個線程試圖移除某個元素,否則也不能(使用任何方法)插入元素;也不能迭代隊列,因爲其中沒有元素可用於迭代。隊列的頭 是嘗試添加到隊列中的首個已排隊插入線程的元素;如果沒有這樣的已排隊線程,則沒有可用於移除的元素並且 poll() 將會返回 null。對於其他 Collection (集合)方法(例如 contains),同步隊列作爲一個空集合這個不允許null(空)元素。
同步隊列類似於 CSP 和 Ada 中使用的 rendezvous 信道。它非常適合於傳遞性設計,在這種設計中,在一個線程中運行的對象要將某些信息、事件或任務傳遞給在另一個線程中運行的對象,它就必須與該對象同步。
threadFactory:
線程工廠,用於在線程池中創建新線程,線程池設計者提供了兩個(其實是一個)線程池工廠給我們使用,一個是defaultThreadFactory(),另一個是privilegedThreadFactory,但是查看源碼我發現:
/**
* Legacy security code; do not use.
*/
public static ThreadFactory privilegedThreadFactory() {
return new PrivilegedThreadFactory();
}
設計者說這是遺留的安全代碼,叫我們不要使用privilegedThreadFactory…,所以一般線程池工廠我們用的是defaultThreadFactory,用法很簡單,直接用Executors.defaultThreadFactory();就可以創建一個默認線程池工廠,或者
handler:
這個參數是一個RejectedExecutionHandler對象,它是一個接口,只有rejectedExecution這個方法,這個參數的作用是當線程池由於任務隊列已滿或別的原因無法執行新任務時,ThreadPoolExecutor就會回調實現了這個接口的類來處理被拒絕任務,它的使用是直接用THreadPoolExecutor.XXX,線程池設計者提供了四個處理方法:
(1).ThreadPoolExecutor.AbortPolicy
直接拋出RejectedExecutionException異常,如果不設置處理策略默認是這個方法進行處理
(2).ThreadPoolExecutor.CallerRunsPolicy
直接在 execute 方法的調用線程中運行被拒絕的任務;如果執行程序已關閉,則會丟棄該任務.
(3).ThreadPoolExecutor.DiscardOldestPolicy
放棄最舊的未處理請求,然後重試 execute;如果執行程序已關閉,則會丟棄該任務。
(4).ThreadPoolExecutor.DiscardPolicy
默認情況下直接丟棄被拒絕的任務。
3.線程池的種類與使用場景:
種類 | 底層 | 任務 | 使用場景 |
---|---|---|---|
newCachedThreadPool | 若有則執行,若無則創建,若超則銷燬 | 大量短期異步任務 | |
newFixedThreadPool | 定池,時間無限,若無則阻塞 | 執行長期的任務,性能好 | |
newSingleThreadExecutor | 單池單線程,時間無限,阻塞執行 | 單任務隊列執行 | |
NewScheduledThreadPool | 定池,時間無限,週期性 | 週期性執行任務的場景 | |
當然,在Alibaba的代碼規範中也說過,建議開發者自己制定更適合自己業務的線程池,當然以上幾種也最好自己來進行創建,這樣更能理解線程池中構造函數參數的含義。
Demo:
一個線程池管理一池的線程
一個task隊列等待池中的空閒線程去執行隊列中的task, task由生產者加入到隊列中,工作線程作爲消費者,只要池中有空閒的等待線程在等閒新的任務,該線程就會在task隊列中消費任務。
ThreadPoolExecutor :
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue );
創建線程池工廠類:
public class PriorityThreadFactory implements ThreadFactory {
public final int mThreadPrio;
/**
*
*工廠類構造函數,傳入優先級
*/
public PriorityThreadFactory(int priority){
this.mThreadPrio = priority;
}
/**
*
*重載父類方法,創建線程
*/
@Override
public Thread newThread(final Runnable runnable) {
Runnable runn = new Runnable() {
@Override
public void run() {
try {
Process.setThreadPriority(mThreadPrio);
}catch (Exception e){
e.printStackTrace();
}
runnable.run();
}
};
return new Thread(runn);
}
}
創建主線程TasksExecutor:
public class MainThreadExecutor implements Executor {
/**
*
* Handler 使用UI線程的Looper
*/
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable runnable) {
// 執行
handler.post(runnable);
}
}
創建一個優先級枚舉類:
public enum ThreadPriority {
/**
* 最低優先級,一般用於預加載數據
*/
LOW,
/**
* 中優先級
*/
MEDIUM,
/**
* 高優先級
*/
HIGHT,
/**
* 立即執行
*/
IMMEDIATE,
}
**PriorityRunnable加入優先級並實現Runnable的方法: **
public class PriorityRunnable implements Runnable {
private final ThreadPriority priority;
/**
*
* 構造函數內傳入優先級
*/
public PriorityRunnable(ThreadPriority threadPriority){
this.priority = threadPriority;
}
/**
*
* 實現線程具體異步操作
*/
@Override
public void run() {
}
public ThreadPriority getPriority(){
return this.priority;
}
}
**PriorityThreadPoolExecutor的類來實現優先級並重載其方法 **
public class PriorityThreadPoolExecutor extends ThreadPoolExecutor {
/**
* 構造函數
* @param corePoolSize
* @param maximumPoolSize
* @param keepAliveTime
* @param unit
* @param workQueue 這裏我們新new出來一個PriorityBlockingQueue
* @param threadFactory
*/
public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new PriorityBlockingQueue<Runnable>(), threadFactory);
}
/**
* 重載複寫sumbit方法,在這裏面我們使用自己創建的Task類
* @param task
* @return
*/
@Override
public Future<?> submit(Runnable task) {
PriorityFutureTask futureTask = new PriorityFutureTask(((PriorityRunnable) task));
execute(futureTask);
return futureTask;
}
/**
* 自己實現的Task類,並使用自己的Runnable,複寫compareTo方法,來調整優先級
*/
public static final class PriorityFutureTask extends FutureTask<PriorityRunnable> implements Comparable<PriorityFutureTask>{
private final PriorityRunnable proRunnable ;
public PriorityFutureTask(PriorityRunnable runnable) {
super(runnable, null);
this.proRunnable = runnable;
}
/**
* 獲取到構造函數的線程優先級及下一個優先級,並比對優先級進行線程排序
*/
@Override
public int compareTo(PriorityFutureTask priorityFutureTask) {
ThreadPriority priority1 = proRunnable.getPriority();
ThreadPriority priority2 = priorityFutureTask.proRunnable.getPriority();
return priority2.ordinal() - priority1.ordinal();
}
}
}
**通過DefaultExecutorSupplier.java封裝幾個線程池的使用,並拋出接口給外部使用: **
public class DefaultExecutorSupplier {
//制定線程池數量,獲取當前進程可用數
public static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
//重量級後臺任務的線程池
private final ThreadPoolExecutor mForBackgroundTasks;
//輕量級後臺任務的線程池
private final ThreadPoolExecutor mForLightWeightBackgroundTasks;
//主線程的線程池
private final Executor mMainThreadExector;
private static DefaultExecutorSupplier mInstance;
/**
* 單例模式返回該類實例化對象
*/
public static DefaultExecutorSupplier getInstance() {
if (null == mInstance) {
synchronized (DefaultExecutorSupplier.class) {
if (null == mInstance) {
mInstance = new DefaultExecutorSupplier();
}
}
}
return mInstance;
}
/**
* 構造函數
* 創建出BackTasks 輕量級Ta sks 主線程Tasks實例化對象
*/
private DefaultExecutorSupplier(){
ThreadFactory backgroundPrioFactory = new PriorityThreadFactory(Process.THREAD_PRIORITY_FOREGROUND);
//BackTasks實例化
//可以進行優先級調整的方法,每種Tasks都可以採用此方法實現
mForBackgroundTasks = new PriorityThreadPoolExecutor(
2,
2,
1,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(),
backgroundPrioFactory
);
//輕量級Tasks實例化
mForLightWeightBackgroundTasks = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
backgroundPrioFactory);
//主線程Tasks實例化
mMainThreadExector = new MainThreadExecutor();
}
/**
* BackTasks拋出,供外部調用
* @return
*/
public ThreadPoolExecutor getmForBackgroundTasks(){
return mForBackgroundTasks;
}
public ThreadPoolExecutor getmForLightWeightBackgroundTasks(){
return mForLightWeightBackgroundTasks;
}
public Executor forMainThreadTasks(){
return mMainThreadExector;
}
}
封裝線程池工具類,供外部使用:
public class ThreadPoolUtils {
/*
*重量級後臺任務線程池,添加返回Future便於手動銷燬線程
*/
public static Future doSomePriorityBackTasks(final String mes, ThreadPriority priority) {
Future future = DefaultExecutorSupplier.getInstance().getmForBackgroundTasks()
.submit(new PriorityRunnable(priority) {
@Override
public void run() {
}
});
return future;
}
/*
*輕量級後臺任務線程池
*/
public static Future doSomeLightWeightBackgroundTasks(final String mes) {
Future future = DefaultExecutorSupplier.getInstance().getmForLightWeightBackgroundTasks()
.execute(new PriorityRunnable(priority) {
@Override
public void run() {
}
});
return future;
}
/*
*主線程任務線程池
*/
public static void doSomeMainThreadWork(final String mes) {
DefaultExecutorSupplier.getInstance().forMainThreadTasks()
.execute(new Runnable() {
@Override
public void run() {
}
});
}
}