【java併發程式設計】執行緒池原理分析及ThreadPoolExecutor原始碼實現
執行緒池簡介:
多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。
假設一個伺服器完成一項任務所需時間為:T1 建立執行緒時間,T2 線上程中執行任務的時間,T3 銷燬執行緒時間。
如果:T1 + T3 遠大於 T2,則可以採用執行緒池,以提高伺服器效能。
一個執行緒池包括以下四個基本組成部分:
1、執行緒池管理器(ThreadPool):用於建立並管理執行緒池,包括 建立執行緒池,銷燬執行緒池,新增新任務;
2、工作執行緒(WorkerThreads):執行緒池中執行緒,在沒有任務時處於等待狀態,可以迴圈的執行任務;
3、任務介面(FutureTask):每個任務必須實現的介面,以供工作執行緒排程任務的執行,它主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀態等;
4、任務佇列(taskQueue):用於存放沒有處理的任務。提供一種緩衝機制。
執行緒池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程式效能的。它把T1,T3分別安排在伺服器程式的啟動和結束的時間段或者一些空閒的時間段,這樣在伺服器程式處理客戶請求時,不會有T1,T3的開銷了。
執行緒池不僅調整T1,T3產生的時間段,而且它還顯著減少了建立執行緒的數目,看一個例子:
假設一個伺服器一天要處理50000個請求,並且每個請求需要一個單獨的執行緒完成。線上程池中,執行緒數一般是固定的,所以產生執行緒總數不會超過執行緒池中執行緒的數目,而如果伺服器不利用執行緒池來處理這些請求則執行緒總數為50000。一般執行緒池大小是遠小於50000。所以利用執行緒池的伺服器程式不會為了建立50000而在處理請求時浪費時間,從而提高效率。
執行緒池原理:
(1)執行緒池的狀態
執行緒池一共有五種狀態:
- RUNNING:接收新任務和處理佇列任務 。
- SHUTDOWN:不接收新任務,但處理佇列裡的任務 。
- STOP:不接收新任務,也不處理佇列任務且中斷正在執行的任務 。
- TIDYING:所有任務都終止,工作執行緒數為0,將執行terminated()方法。
- TERMINATED: terminated() 執行完成。
以下是執行緒池狀態的轉換過程:
這裡看出SHUTDOWN和STOP的區別:
當執行緒池處於SHUTDOWN狀態,那麼執行緒池拒絕接受新的任務,但會繼續執行阻塞佇列中的任務。
當執行緒池處於STOP狀態,不接收新任務,也不處理佇列任務且中斷正在執行的任務 。
(2)執行緒池處理任務流程
當向執行緒池提交一個任務之後,執行緒池是如何處理這個任務的呢?本節來看一下執行緒池
的主要處理流程,處理流程圖所示。
從上圖可以看出,執行緒池對於任務的處理流程:
- 核心執行緒池未滿,則建立執行緒加入核心執行緒池執行任務。
- 核心執行緒池滿了,但是阻塞佇列未滿,就將任務加入阻塞佇列,等待工作執行緒執行。
- 阻塞佇列滿了,但是執行緒池還沒有滿,就建立執行緒加入執行緒池執行任務。
- 如果執行緒池滿了,就按照策略執行拒絕執行的任務。
(3)執行緒回收的情況
- 執行緒池狀態為STOP
- 執行緒池狀態為SHUTDOWN且任務阻塞佇列為空。
- 工作執行緒超過了最大執行緒數maximumPoolsize
- 獲取超時,超過了 執行緒池所維護的執行緒的活動時間。
第四點單獨說一下:keepAliveTime : 執行緒池所維護的執行緒的活動時間。如果超過了該時間範圍,而執行緒還是空閒的,那麼該執行緒將會被回收。不再由執行緒池所維護。以下是官方的一些說明: 如果當前執行緒池數大於核心執行緒池數,且這些執行緒是空閒的超過所設定的存活時間keepAliveTime,那麼這些多出來的執行緒則會被終止,可以降低執行緒資源消耗。通過設定儘可能大的過期時間來阻止空閒執行緒被銷燬,可手動呼叫setKeepAliveTime來設定。預設情況下,該策略只能適用於大於核心執行緒數的執行緒,但是可以通過設定ThreadPoolExecutor.allowCoreThreadTimeOut(boolean),則該策略也適用於核心執行緒數。
繼承關係:
ThreadPoolExecutor 原始碼分析:
內功心法:欄位分析
public class ThreadPoolExecutor extends AbstractExecutorService {
//共享變數 32位整數,左邊3位是執行緒池狀態,後面29位是執行緒池中的工作執行緒數
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//工作執行緒位數29位
private static final int COUNT_BITS = Integer.SIZE - 3;
//00011111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 五種執行緒池狀態,前面分析過了
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
//獲取執行緒池狀態 c&11100000000000000000000000000000 就是獲取前三位的值
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
}
//任務阻塞佇列,生產消費者模式
private final BlockingQueue<Runnable> workQueue;
//同步鎖,通過鎖來實現多執行緒同步。
private final ReentrantLock mainLock = new ReentrantLock();
//工作執行緒池
private final HashSet<Worker> workers = new HashSet<Worker>();
private final Condition termination = mainLock.newCondition();
//曾經最大執行緒池大小
private int largestPoolSize;
//完成任務總數
private long completedTaskCount;
//執行緒工廠,生產執行緒
private volatile ThreadFactory threadFactory;
//拒絕策略
private volatile RejectedExecutionHandler handler;
//執行緒空閒情況存活時間
private volatile long keepAliveTime;
//允許回收核心執行緒
private volatile boolean allowCoreThreadTimeOut;
//核心執行緒數
private volatile int corePoolSize;
//最大執行緒數
private volatile int maximumPoolSize;
以下是引數說明:
corePoolSize : 執行緒池的核心執行緒數。該引數並不是初始化時候寫死了,執行緒池物件構造完成以後也能通過它提供的一些方法動態修改,ThreadPoolExecutor.setCorePoolSize。
maximumPoolSize : 執行緒池的最大執行緒數,只有當任務佇列滿了才會建立新的執行緒。該引數並不是初始化時候寫死了,執行緒池物件構造完成以後也能通過它提供的一些方法動態修改,ThreadPoolExecutor.setMaximumPoolSize。
keepAliveTime : 執行緒池所維護的執行緒的活動時間。如果超過了該時間範圍,而執行緒還是空閒的,那麼該執行緒將會被回收。不再由執行緒池所維護。以下是官方的一些說明: 如果當前執行緒池數大於核心執行緒池數,且這些執行緒是空閒的超過所設定的存活時間keepAliveTime,那麼這些多出來的執行緒則會被終止,可以降低執行緒資源消耗。通過設定儘可能大的過期時間來阻止空閒執行緒被銷燬,可手動呼叫setKeepAliveTime來設定。預設情況下,該策略只能適用於大於核心執行緒數的執行緒,但是可以通過設定ThreadPoolExecutor.allowCoreThreadTimeOut(boolean),則該策略也適用於核心執行緒數。
threadFactory : 傳入一個執行緒工廠。通過該執行緒工廠可以建立執行緒。它建立的所有執行緒都是通過同樣的ThreadGroup和同樣的NORM_PRIORITY和non-daemon狀態。通過提供的不同ThreadFactory,可以掌握修改執行緒名字,執行緒組,優先順序,守護狀態等。如果執行緒池呼叫執行緒工廠建立一個執行緒失敗時,則返回一個null。且executor會繼續,但是可能不會執行任何任務。 如果我們自己重寫封裝了一遍執行緒工廠,還有個好處就是可以通過該執行緒工廠例項維護所有由它建立的執行緒。
人劍合一:原始碼函式分析
submit(Callable or Runnable)
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
//採用介面卡模式將任務封裝
RunnableFuture<T> ftask = newTaskFor(task);
//呼叫子類ThreadPoolExecutor的實現
execute(ftask);
//返回任務控制代碼,該控制代碼可以獲取任務執行情況和結果,也可以取消任務。
return ftask;
}
execute(Runnable command)
//execute()對應了執行緒池執行任務的四步流程,前面已經說過
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.核心執行緒池不滿
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.核心執行緒池滿了,任務阻塞佇列不滿
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//3.任務阻塞佇列滿了,執行緒池不滿
else if (!addWorker(command, false))
//4.執行緒池滿了,按照策略處理無法執行的任務。
reject(command);
}
boolean addWorker(Runnable firstTask, boolean core)
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 1.如果執行緒池狀態為SHUTDOWN,firstTask 為空(沒有新任務)且任務阻塞佇列不為空,才建立Worker去執行阻塞佇列任務,否則返回false
// 2.如果執行緒池狀態>=STOP,返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
//3.如果工作執行緒數大於(是核心? 核心執行緒數 : 最大執行緒數),返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
4.如果逃過了上面三個鬼門關,就來到了這裡,建立新的Worker,並啟動這個Worker的執行緒
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
int rs = runStateOf(c);
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
當執行execute()新增任務時,拒絕建立新執行緒的情況:
- 如果執行緒池狀態為SHUTDOWN,firstTask 為空(沒有新任務)且任務阻塞佇列不為空,才建立Worker去執行阻塞佇列任務,否則返回false。
- 如果執行緒池狀態>=STOP,返回false。
- 如果工作執行緒數大於(是核心? 核心執行緒數 : 最大執行緒數),返回false。
如果執行緒數大於核心執行緒數,execute()就會讓任務進阻塞佇列,如果大於最大執行緒數,或者執行緒池狀態>=SHUTDOWN那麼會按照策略處理無法執行的任務。
Worker類
/**
Worker類裡封裝了Thread和任務Runnable,還有completedTasks。可以注意到建立一個Thread時候把this傳入,這樣的話如果我呼叫Worker.thread.start()就相當於該執行緒會執行Worker裡的run方法了。completedTasks是用來記錄該執行緒完成了多少個任務(非整個執行緒池)。
注意該Worker繼承了AQS同步基礎器,它主要實現了互斥鎖的功能,但是這個互斥鎖和ReentrantLock有點不同,該實現是不允許執行緒重入獲取鎖的。下面說說為什麼要實現鎖功能和非重入:
1.lock方法主要用在標明當前執行緒正在執行任務中,而private interruptIdleWorkers 方法需要使用tryLock來判斷當前執行緒是否正在執行任務,如果非執行任務狀態則表明可能是正在獲取任務,那麼該執行緒屬於空閒狀態,可以被中斷。
2.看回答1可以知道這通過ReentrantLock也能實現,但是如果我們在提交一個任務給執行緒池(實現一個Runnable),如果該
任務裡面呼叫了和interruptIdleWorkers相關的,需要中斷當前可獲取鎖(代表空閒)的執行緒。如果我們不使用非重入鎖,這個任務執行緒就會給中斷,從而導致一些奇怪的問題。
**/
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
//儲存的執行緒
final Thread thread;
//初始任務
Runnable firstTask;
//完成任務數
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/**
主要看這裡,其他的都是關於AQS的實現。
**/
public void run() {
runWorker(this);
}
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
final void runWorker(Worker w)
final void runWorker(Worker w) {
//1.這裡獲取當前執行執行緒,就是Worker所封裝的Thread(因為是通過該Thread啟動的,然後執行自身的run方法)
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//2.如果存在第一個任務則直接執行該任務,否則從任務佇列裡阻塞獲取任務
while (task != null || (task = getTask()) != null) {
w.lock();
//2.如果執行緒池已經STOP,確保當前工作執行緒被中斷,否則,確保執行緒沒被中斷。
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//3.執行任務前的鉤子方法
beforeExecute(wt, task);
Throwable thrown = null;
try {
//4.這裡的run()方法,會將當前執行緒傳遞給任務,參見我的關於FutureTask文章
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//5.執行任務後的鉤子方法
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
/* 6.執行到這裡代表該執行緒已被終止,將被回收(從執行緒池的workers裡刪除該執行緒)。
這個方法同時也代表了當執行緒超出了空閒時間後,將不再由執行緒池維護,而是被GC回收。
具體可以看getTask。由於getTask是以阻塞方式從阻塞佇列獲取任務,可以通過阻塞獲取時候
設定一個阻塞時間來達到 keepAliveTime空閒功能*/
processWorkerExit(w, completedAbruptly);
}
}
Runnable getTask()
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
retry:
for (;;) {
int c = ctl.get();
//1.獲取當前執行緒池的狀態
int rs = runStateOf(c);
// 2。如果執行緒池已被shutdown或者由於其他原因關閉,那麼則終止該執行緒,返回null,
//最後就會走processWorkerExit方法 了
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
boolean timed; // Are workers subject to culling?
for (;;) {
//3.獲取執行緒池當前的執行緒數(worker數量則代表執行緒數)
int wc = workerCountOf(c);
//4.判斷是否需要採取設定 阻塞時間的方式獲取任務.如果核心執行緒也需要空閒回收
//或者當前執行緒數量已經超越了核心執行緒數,那麼都需要採取阻塞時間獲取任務方式。
timed = allowCoreThreadTimeOut || wc > corePoolSize;
//5.判斷是否需要跳出迴圈,迴圈僅僅只是為了cas修改減少執行緒池的執行緒數。
if (wc <= maximumPoolSize && ! (timedOut && timed))
break;
// 6.執行到這裡代表阻塞獲取任務超時,keepAlivetime時間到了。該執行緒將被回收
if (compareAndDecrementWorkerCount(c))
return null;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
try {
//7.如果需要採用阻塞形式獲取,那麼就poll設定阻塞時間,否則take無限期等待。
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
getTask()有四種情況會返回null:
- 執行緒池終止:state>=STOP。
- 執行緒池狀態為SHUTDOWN,而且任務阻塞佇列為空。
- 工作執行緒數超過了最大執行緒數maximumPoolSize。
- 獲取任務超時,也就是該工作執行緒空閒時間超時。
注意第四種情況:
當工作執行緒超過keepAliveTime空閒時間,getTask()就會返回null,runWoker()就會執行
finally{ processWorkerExit(w, completedAbruptly);}執行到這裡代表該執行緒已被終止,將被回收(從執行緒池的workers裡刪除該執行緒)。 這個方法同時也代表了當執行緒超出了空閒時間後,將不再由執行緒池維護,而是被GC回收。我們可以設定keepAliveTime來控制回收工作執行緒,注意這裡是回收的不是核心執行緒,而是核心執行緒之外的工作執行緒。
timed = allowCoreThreadTimeOut || wc > corePoolSize;這條語句是getTask()函式中的關鍵語句。
Runnable r = timed ?workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :workQueue.take();
可以看出如果我們設定了allowCoreThreadTimeOut 為true,那麼執行緒池也會回收核心執行緒。
到此我們就把執行緒池講完了。整個函式分析流程就是一條呼叫的路線,思路很清晰吧!
合理地配置執行緒池
要想合理地配置執行緒池,就必須首先分析任務特性,可以從以下幾個角度來分析。
·任務的性質:CPU密集型任務、IO密集型任務和混合型任務。
·任務的優先順序:高、中和低。
·任務的執行時間:長、中和短。
·任務的依賴性:是否依賴其他系統資源,如資料庫連線。
性質不同的任務可以用不同規模的執行緒池分開處理。CPU密集型任務應配置儘可能小的
執行緒,如配置Ncpu+1個執行緒的執行緒池。由於IO密集型任務執行緒並不是一直在執行任務,則應配
置儘可能多的執行緒,如2*Ncpu。混合型的任務,如果可以拆分,將其拆分成一個CPU密集型任務
和一個IO密集型任務,只要這兩個任務執行的時間相差不是太大,那麼分解後執行的吞吐量
將高於序列執行的吞吐量。如果這兩個任務執行時間相差太大,則沒必要進行分解。可以通過
Runtime.getRuntime().availableProcessors()方法獲得當前裝置的CPU個數。
優先順序不同的任務可以使用優先順序佇列PriorityBlockingQueue來處理。它可以讓優先順序高
的任務先執行。
注意 如果一直有優先順序高的任務提交到佇列裡,那麼優先順序低的任務可能永遠不能
執行。
執行時間不同的任務可以交給不同規模的執行緒池來處理,或者可以使用優先順序佇列,讓
執行時間短的任務先執行。
依賴資料庫連線池的任務,因為執行緒提交SQL後需要等待資料庫返回結果,等待的時間越
長,則CPU空閒時間就越長,那麼執行緒數應該設定得越大,這樣才能更好地利用CPU。
建議使用有界佇列。
有界佇列能增加系統的穩定性和預警能力,可以根據需要設大一點
兒,比如幾千。有一次,我們系統裡後臺任務執行緒池的佇列和執行緒池全滿了,不斷丟擲拋棄任
務的異常,通過排查發現是資料庫出現了問題,導致執行SQL變得非常緩慢,因為後臺任務線
程池裡的任務全是需要向資料庫查詢和插入資料的,所以導致執行緒池裡的工作執行緒全部阻
塞,任務積壓線上程池裡。如果當時我們設定成無界佇列,那麼執行緒池的佇列就會越來越多,
有可能會撐滿記憶體,導致整個系統不可用,而不只是後臺任務出現問題。當然,我們的系統所
有的任務是用單獨的伺服器部署的,我們使用不同規模的執行緒池完成不同型別的任務,但是
出現這樣問題時也會影響到其他任務。
執行緒池的監控
如果在系統中大量使用執行緒池,則有必要對執行緒池進行監控,方便在出現問題時,可以根
據執行緒池的使用狀況快速定位問題。可以通過執行緒池提供的引數進行監控,在監控執行緒池的
時候可以使用以下屬性。
·taskCount:執行緒池需要執行的任務數量。
·completedTaskCount:執行緒池在執行過程中已完成的任務數量,小於或等於taskCount。
·largestPoolSize:執行緒池裡曾經建立過的最大執行緒數量。通過這個資料可以知道執行緒池是
否曾經滿過。如該數值等於執行緒池的最大大小,則表示執行緒池曾經滿過。
·getPoolSize:執行緒池的執行緒數量。如果執行緒池不銷燬的話,執行緒池裡的執行緒不會自動銷
毀,所以這個大小隻增不減。
·getActiveCount:獲取活動的執行緒數。
通過擴充套件執行緒池進行監控。可以通過繼承執行緒池來自定義執行緒池,重寫執行緒池的
beforeExecute、afterExecute和terminated方法,也可以在任務執行前、執行後和執行緒池關閉前執
行一些程式碼來進行監控。例如,監控任務的平均執行時間、最大執行時間和最小執行時間等。
這幾個方法線上程池裡是空方法。
protected void beforeExecute(Thread t, Runnable r) { }
參考文章
相關推薦
【java併發程式設計】執行緒池原理分析及ThreadPoolExecutor原始碼實現
執行緒池簡介: 多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。 假設一個伺服器完成一項任務所需時間為:T1 建立執行緒時間,T2 線上程中執行任務的時間,T3 銷燬執行緒時間。
java併發程式設計一一執行緒池原理分析(三)
合理的設定執行緒池的大小 接著上一篇探討執行緒留下的尾巴。如果合理的設定執行緒池的大小。 要想合理的配置執行緒池的大小、首先得分析任務的特性,可以從以下幾個角度分析: 1、任務的性質:CPU密集型任務、IO密集型任務、混合型任務等; 2、任務的優先順序:高、中、低; 3、任務的執行時
java併發程式設計一一執行緒池原理分析(二)
2、執行緒池 1、什麼是執行緒池 Java中的執行緒池是運用場景最多的併發框架,幾乎所有需要非同步或併發執行任務的程式都可以使用執行緒池。 在開發工程中,合理的使用執行緒池能夠帶來3個好處。 第一:降低資源的消耗,通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗 第二:提
java併發程式設計一一執行緒池原理分析(一)
1、併發包 1、CountDownLatch(計數器) CountDownLatch 類位於 java.util.concurrent 包下,利用它可以實現類似於計數器的功能。 比如有一個任務A,它要等待其他4個任務執行完成之後才能執行,此時就可以利用CountDownLatch
Java併發程式設計之執行緒池(三)
一.介紹 Java通過Executors提供四種執行緒池,分別為: (1)newCachedThreadPool:建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。 (2)newFixedThreadPool: 建立一個定長執行緒池,可控制
Java併發(二十一):執行緒池實現原理 Java併發(十八):阻塞佇列BlockingQueue Java併發(十八):阻塞佇列BlockingQueue Java併發程式設計:執行緒池的使用
一、總覽 執行緒池類ThreadPoolExecutor的相關類需要先了解: (圖片來自:https://javadoop.com/post/java-thread-pool#%E6%80%BB%E8%A7%88) Executor:位於最頂層,只有一個 execute(Runnab
Java併發程式設計:執行緒池的使用
如果併發的執行緒數量很多,並且每個執行緒都是執行一個時間很短的任務就結束了,這樣頻繁建立執行緒就會大大降低系統的效率,因為頻繁建立執行緒和銷燬執行緒需要時間。 那麼有沒有一種辦法使得執行緒可以複用,就是執行完一個任務,並不被銷燬,而是可以繼續執行其他的任務? 在J
Java併發程式設計:執行緒池的使用(轉載)
轉載自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java併發程式設計:執行緒池的使用 在前面的文章中,我們使用執行緒的時候就去建立一個執行緒,這樣實現起來非常簡便,但是就會有一個問題: 如果併發的執行緒數量很多,並且每個執行緒都是執行
JAVA併發程式設計:執行緒池 ThreadPoolExecutor
生活 前期追深度,否則會華而不實,後期追廣度,否則會坐井觀天; 前言 在前面,我們已經對Thread有了比較深入的瞭解,並且已經學會了通過new Thread()來建立一個執行緒,並通過start方法來啟動一個執行緒,這種方法非常簡單,同樣也存在弊端: 1、每次通過new Thr
java併發程式設計之執行緒池
前言 本文介紹幾種java常用的執行緒池如:FixedThreadPool,ScheduledThreadPool,CachedThreadPool等執行緒池,並分析介紹Executor框架,做到“知其然”:會用執行緒池,正確使用執行緒池。並且“知其所以然”:
Java併發程式設計之執行緒池、Callable和Future使用
知識儲備 收藏幾篇好文章: 目錄結構 Callable和Future使用 執行緒池使用 Callable和Future使用 多執行緒實現方式很多,分為兩類:1、沒有返回值的;2、有返回值的。 針對“沒有返回值的”這類可以參
JAVA併發程式設計:執行緒池Executors
Java中對執行緒池提供了很好的支援,有了執行緒池,我們就不需要自已再去建立執行緒。如果併發的執行緒數量很多,並且每個執行緒都是執行一個時間很短的任務就結束了,頻繁建立執行緒就會大大降低系統的效率,因為頻繁建立執行緒和銷燬執行緒需要時間。JAVA的執行緒池中的執行緒可以在執
JAVA 併發程式設計-基於執行緒池設計的ScheduledExecutor(八)
上篇部落格《JAVA 併發程式設計-執行緒池(七)》中曾介紹到newScheduledThreadPool(intcorePoolSize),建立corePoolSize大小的執行緒池。此執行緒池支援定時以及週期性執行任務的需求。 接下來我們一起來分析一下Jav
【Android 併發程式設計】執行緒間通訊的三種基本方式,android執行緒
1. 使用管道流Pipes “管道”是java.io包的一部分。它是Java的特性,而不是Android特有的。一條“管道”為兩個執行緒建立一個單向的通道。生產者負責寫資料,消費者負責讀取資料。 下面是一個使用管道流進行通訊的例子。 public class Pip
【Java進階】執行緒池深入理解
Java併發程式設計:執行緒池的使用在前面的文章中,我們使用執行緒的時候就去建立一個執行緒,這樣實現起來非常簡便,但是就會有一個問題:如果併發的執行緒數量很多,並且每個執行緒都是執行一個時間很短的任務就結束了,這樣頻繁建立執行緒就會大大降低系統的效率,因為頻繁建立執行緒和銷燬執行緒需要時間。那
Java併發程式設計之執行緒池的使用
1. 為什麼要使用多執行緒? 隨著科技的進步,現在的電腦及伺服器的處理器數量都比較多,以後可能會越來越多,比如我的工作電腦的處理器有8個,怎麼檢視呢? 計算機右鍵--屬性--裝置管理器,開啟屬性視窗,然後點選“裝置管理器”,在“處理器”下可看到所有的處理器: 也可以通過以下Java程式碼獲取到處理器的
Java併發程式設計:執行緒池ThreadPoolExecutor
多執行緒的程式的確能發揮多核處理器的效能。雖然與程序相比,執行緒輕量化了很多,但是其建立和關閉同樣需要花費時間。而且執行緒多了以後,也會搶佔記憶體資源。如果不對執行緒加以管理的話,是一個非常大的隱患。而執行緒池的目的就是管理執行緒。當你需要一個執行緒時,你就可以拿一個空閒執行緒去執行任務,當任務執行完後,
java併發包&執行緒池原理分析&鎖的深度化
併發包 同步容器類 Vector與ArrayList區別 1.ArrayList是最常用的List實現類,內部是通過陣列實現的,它允許對元素進行快速隨機訪問。陣列的缺點是每個元素之間不能有間隔,當陣列大小不滿足時需要增加儲存能力,就要講已經有陣列的資料
【Java併發程式設計】阿里最喜歡問的幾道執行緒池的面試題?
### 引言 上一篇文章我們有介紹過執行緒池的一個基本執行流程[《【Java併發程式設計】面試必備之執行緒池》](https://mp.weixin.qq.com/s/9l2l2whLgYPrBbsGv3Xw6w)以及它的7個核心引數,以及每個引數的作用、以及如何去使用執行緒池 還留了幾個小問題。。建議看這篇
【Java併發程式設計】之六:Runnable和Thread實現多執行緒的區別(含程式碼)
Java中實現多執行緒有兩種方法:繼承Thread類、實現Runnable介面,在程式開發中只要是多執行緒,肯定永遠以實現Runnable介面為主,因為實現Runnable介面相比繼承Th