Java多线程知识小抄集(二)
歡迎支持筆者新作:《深入理解Kafka:核心設(shè)計(jì)與實(shí)踐原理》和《RabbitMQ實(shí)戰(zhàn)指南》,同時(shí)歡迎關(guān)注筆者的微信公眾號:朱小廝的博客。
歡迎跳轉(zhuǎn)到本文的原文鏈接:https://honeypps.com/java/java-multiple-thread-summary-2/
本文主要整理博主遇到的Java多線程的相關(guān)知識點(diǎn),適合速記,故命名為“小抄集”。本文沒有特別重點(diǎn),每一項(xiàng)針對一個(gè)多線程知識做一個(gè)概要性總結(jié),也有一些會(huì)帶一點(diǎn)例子,習(xí)題方便理解和記憶。
27. ConcurrentHashMap
ConcurrentHashMap是線程安全的HashMap,內(nèi)部采用分段鎖來實(shí)現(xiàn),默認(rèn)初始容量為16,裝載因子為0.75f,分段16,每個(gè)段的HashEntry<K,V>[]大小為2。**鍵值都不能為null。**每次擴(kuò)容為原來容量的2倍,ConcurrentHashMap不會(huì)對整個(gè)容器進(jìn)行擴(kuò)容,而只對某個(gè)segment進(jìn)行擴(kuò)容。在獲取size操作的時(shí)候,不是直接把所有segment的count相加就可以可到整個(gè)ConcurrentHashMap大小,也不是在統(tǒng)計(jì)size的時(shí)候把所有的segment的put, remove, clean方法全部鎖住,這種方法太低效。在累加count操作過程中,之前累加過的count發(fā)生變化的幾率非常小,所有ConcurrentHashMap的做法是先嘗試2(RETRIES_BEFORE_LOCK)次通過不鎖住Segment的方式統(tǒng)計(jì)各個(gè)Segment大小,如果統(tǒng)計(jì)的過程中,容器的count發(fā)生了變化,再采用加鎖的方式來統(tǒng)計(jì)所有的Segment的大小。
28. 線程安全的非阻塞隊(duì)列
非阻塞隊(duì)列有ConcurrentLinkedQueue, ConcurrentLinkedDeque。**元素不能為null。**以ConcurrentLinkedQueue為例,有頭head和尾tail兩個(gè)指針,遵循FIFO的原則進(jìn)行入隊(duì)和出隊(duì),方法有add(E e), peek()取出不刪除, poll()取出刪除, remove(Object o),size(), contains(Object o), addAll(Collection c), isEmpty()。ConcurrentLinkedDeque是雙向隊(duì)列,可以在頭和尾兩個(gè)方向進(jìn)行相應(yīng)的操作。
29. 阻塞隊(duì)列
阻塞隊(duì)列(BlockingQueue)是一個(gè)支持兩個(gè)附加操作的隊(duì)列。這兩個(gè)附加的操作支持阻塞的插入和移除方法。
支持阻塞的插入方法:意思是當(dāng)隊(duì)列滿時(shí),隊(duì)列會(huì)阻塞插入元素的線程,直到隊(duì)列不滿。
支持阻塞的移除方法:意思是隊(duì)列為空時(shí),獲取元素的線程會(huì)等待隊(duì)列變?yōu)榉强铡?br /> 任何阻塞隊(duì)列中的元素都不能為null.
30. 阻塞隊(duì)列的插入和移除操作處理方式:
| 入隊(duì) | add(e) | offer(e) | put(e) | offer(e,timeout,unit) |
| 出隊(duì) | remove() | poll() | take() | poll(timeout,unit) |
| 查看 | element() | peek() | 無 | 無 |
如果是無界隊(duì)列,隊(duì)列不可能出現(xiàn)滿的情況,所以使用put或offer方法永遠(yuǎn)不會(huì)被阻塞,而且使用offer方法時(shí),該方法永遠(yuǎn)返回true.
31. Java里的阻塞隊(duì)列
ArrayBlockingQueue:一個(gè)由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列。
LinkedeBlockingQueue:一個(gè)有鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列。
PriorityBlockingQueue:一個(gè)支持優(yōu)先級排序的無界阻塞隊(duì)列
DelayQueue:一個(gè)使用優(yōu)先級隊(duì)列實(shí)現(xiàn)的無界阻塞隊(duì)列。
SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。
LinkedTransferQueue:一個(gè)由鏈表結(jié)構(gòu)組成的無界阻塞隊(duì)列。
LinkedBlockingDeque:一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列。
###32. ArrayBlockingQueue
此隊(duì)列按照FIFO的原則對元素進(jìn)行排序,可以設(shè)定為公平ArrayBlockingQueue(int capacity, boolean fair),默認(rèn)為不公平。初始化時(shí)必須設(shè)定容量大小ArrayBlockingQueue(int capactiy)。
33. LinkedBlockingQueue
與ArrayBlockingQueue一樣,按照FIFO原則進(jìn)行排序,與ArrayBlockingQueue不同的是內(nèi)部實(shí)現(xiàn)是一個(gè)鏈表結(jié)構(gòu),且不能設(shè)置為公平的。默認(rèn)和最大長度為Integer.MAX_VALUE。
34. PriorityBlockingQueue
是一個(gè)支持優(yōu)先級的無界阻塞隊(duì)列,默認(rèn)初始容量為11,默認(rèn)情況下采用自然順序升序排列,不能保證同優(yōu)先級元素的順序。內(nèi)部元素要么實(shí)現(xiàn)Comparable接口,要么在初始化的時(shí)候指定構(gòu)造函數(shù)的Comparator來對元素進(jìn)行排序,有關(guān)Comparable與Comparator的細(xì)節(jié)可以參考:Comparable與Comparator淺析。
35. DelayQueue
DelayQueue是一個(gè)支持延時(shí)獲取元素的無界阻塞隊(duì)列。內(nèi)部包含一個(gè)PriorityQueue來實(shí)現(xiàn),隊(duì)列中的元素必須實(shí)現(xiàn)Delay接口,在創(chuàng)建元素時(shí)可以指定多久才能從隊(duì)列中獲取當(dāng)前元素。只有在延遲期滿時(shí)才能從隊(duì)列中提取元素。
DelayQueue非常有用,可以將DelayQueue運(yùn)用在下面應(yīng)用場景。
- 緩存系統(tǒng)的設(shè)計(jì):可以用DelayQueue保存緩存元素的有效期,使用一個(gè)線程循環(huán)查詢DelayQueue,一旦能從DelayQueue中獲取元素時(shí),表示緩存有效期到了。
- 定時(shí)任務(wù)調(diào)度:使用DelayQueue保存當(dāng)天將會(huì)執(zhí)行的任務(wù)和執(zhí)行時(shí)間,一旦從DelayQueue中獲取到任務(wù)就開始執(zhí)行,比如TimerQueue就是使用DelayQueue實(shí)現(xiàn)的。
36. SynchronousQueue
是一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列,每一個(gè)put操作必須等待一個(gè)take操作,否則不能繼續(xù)添加元素,非常適合傳遞性場景。支持公平訪問隊(duì)列。默認(rèn)情況下線程采用非公平策略訪問隊(duì)列。
37. LinkedTransferQueue
是一個(gè)由鏈表結(jié)構(gòu)組成的無界阻塞TransferQueue隊(duì)列。相對于其他阻塞隊(duì)列,LinkedTransferQueue多了tryTransfer和transfer方法。
transfer方法:如果當(dāng)前有消費(fèi)者正在等待接收元素(消費(fèi)者使用take()或者帶時(shí)間限制的poll方法時(shí)),transfer方法可以把生產(chǎn)者傳入的元素立刻transfer給消費(fèi)者,如果沒有消費(fèi)者在等待接收元素,transfer方法會(huì)將元素存放在隊(duì)列的tail節(jié)點(diǎn),并等到該元素被消費(fèi)者消費(fèi)了才返回。
tryTransfer方法:用來試探生產(chǎn)者傳入的元素是否能直接傳給消費(fèi)者。如果沒有消費(fèi)者等待接收元素,則返回false。和transfer方法的區(qū)別是tryTransfer方法無論消費(fèi)者是否接收,方法立刻返回,而transfer方法是必須等到消費(fèi)者消費(fèi)了才返回。
38. LinkedBlockingDeque
LinkedBlockingDeque是一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列。所謂雙向隊(duì)列是指可以從隊(duì)列的兩端插入和移除元素。雙向隊(duì)列因?yàn)槎嗔艘粋€(gè)操作隊(duì)列的入口,在多線程同時(shí)入隊(duì)時(shí),也就減少了一半的競爭。相對其他的阻塞隊(duì)列,LinkedBlockingDeque多了addFirst, addLast, offerFirst, offerLast, peekFirst, peekLast等方法。
39. Fork/Join框架
Fork/Join框架是JDK7提供的一個(gè)用于并行執(zhí)行任務(wù)的框架,是一個(gè)把大任務(wù)切分為若干子任務(wù)并行的執(zhí)行,最終匯總每個(gè)小任務(wù)后得到大任務(wù)結(jié)果的框架。我們再通過Fork和Join來理解下Fork/Join框架。Fork就是把一個(gè)大任務(wù)劃分成為若干子任務(wù)并行的執(zhí)行,Join就是合并這些子任務(wù)的執(zhí)行結(jié)果,最后得到這個(gè)大任務(wù)的結(jié)果。
使用Fork/Join框架時(shí),首先需要?jiǎng)?chuàng)建一個(gè)ForkJoin任務(wù),它提供在任務(wù)中執(zhí)行fork()和join操作的機(jī)制。通常情況下,我們不需要直接繼承ForkJoinTask,只需要繼承它的子類,Fork/Join框架提供了兩個(gè)子類:RecursiveAction用于沒有返回結(jié)果的任務(wù);RecursiveTask用于有返回結(jié)果的任務(wù)。ForkJoinTask需要通過ForkJoinPool來執(zhí)行。
任務(wù)分割出的子任務(wù)會(huì)添加到當(dāng)前工作線程所維護(hù)的雙端隊(duì)列中,進(jìn)入隊(duì)列的頭部。當(dāng)一個(gè)工作線程的隊(duì)列里暫時(shí)沒有任務(wù)時(shí),它會(huì)隨機(jī)從其他工作線程的隊(duì)列的尾部獲取一個(gè)任務(wù)。(工作竊取算法work-stealing)
示例:計(jì)算1+2+3+…+100的結(jié)果。
import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.RecursiveTask;public class CountTask extends RecursiveTask<Integer> {private static final int THRESHOLD = 10;private int start;private int end;public CountTask(int start, int end){super();this.start = start;this.end = end;}@Overrideprotected Integer compute(){int sum = 0;boolean canCompute = (end-start) <= THRESHOLD;if(canCompute){for(int i=start;i<=end;i++){sum += i;}}else{int middle = (start+end)/2;CountTask leftTask = new CountTask(start,middle);CountTask rightTask = new CountTask(middle+1,end);leftTask.fork();rightTask.fork();int leftResult = leftTask.join();int rightResult = rightTask.join();sum = leftResult+rightResult;}return sum;}public static void main(String[] args){ForkJoinPool forkJoinPool = new ForkJoinPool();CountTask task = new CountTask(1,100);Future<Integer> result = forkJoinPool.submit(task);try{System.out.println(result.get());}catch (InterruptedException | ExecutionException e){e.printStackTrace();}if(task.isCompletedAbnormally()){System.out.println(task.getException());}} }40. 原子類
Java中Atomic包里一共提供了12個(gè)類,屬于4種類型的原子更新方式,分別是原子更新基本類型、原子更新數(shù)組、原子更新引用、原子更新屬性(字段)。Atomic包里的類基本都是使用Unsafe實(shí)現(xiàn)的包裝類。
1)原子更新基本類型:AtomicBoolean,AtomicInteger, AtomicLong.
2)原子更新數(shù)組:AtomicIntegerArray,AtomicLongArray, AtomicReferenceArray.
3)原子更新引用類型:AtomicReference, AtomicStampedReference, AtomicMarkableReference.
4 ) 原子更新字段類型:AtomicReferenceFieldUpdater, AtomicIntegerFieldUpdater, AtomicLongFieldUpdater.
41. 原子更新基本類型
AtomicBoolean,AtomicInteger, AtomicLong三個(gè)類提供的方法類似,以AtomicInteger為例:有int addAndGet(int delta), boolean compareAndSet(int expect, int update), int getAndIncrement(), void lazySet(int newValue),int getAndSet(int newValue)。其中大多數(shù)的方法都是調(diào)用compareAndSet方法實(shí)現(xiàn)的,譬如getAndIncrement():
public final int getAndIncrement() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return current;}}public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}sun.misc.Unsafe只提供三種CAS方法:compareAndSwapObject, compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源碼,發(fā)現(xiàn)它是先把Boolean轉(zhuǎn)換成整形,再使用compareAndSwapInt進(jìn)行CAS,原子更新char,float,double變量也可以用類似的思路來實(shí)現(xiàn)。
42. 原子更新數(shù)組
以AtomicIntegerArray為例,此類主要提供原子的方式更新數(shù)組里的整形,常用方法如下:
int addAndGet(int i, int delta):以原子的方式將輸入值與數(shù)組中索引i的元素相加。
boolean compareAndSet(int i, int expect, int update):如果當(dāng)前值等于預(yù)期值,則以原子方式將數(shù)組位置i的元素設(shè)置成update值。
AtomicIntegerArray的兩個(gè)構(gòu)造方法:
AtomicIntegerArray(int length):指定數(shù)組的大小,并初始化為0
AtomicIntegerArray(int [] array):對給定的數(shù)組進(jìn)行拷貝。
案例:
運(yùn)行結(jié)果:2 9 2
43. CountDownLatch
CountDownLatch允許一個(gè)或多個(gè)線程等待其他線程完成操作。CountDownLatch的構(gòu)造函數(shù)接收一個(gè)int類型的參數(shù)作為計(jì)數(shù)器,如果你想等待N個(gè)點(diǎn)完成,這里就傳入N(CountDownLatch(int count))。
CountDownLatch的方法有:await(), await(long timeout, TimeUnit unit), countDown(), getCount()等。
計(jì)數(shù)器必須大于等于0,只是等于0的時(shí)候,計(jì)數(shù)器就是零,調(diào)用await方法時(shí)不會(huì)阻塞當(dāng)前線程。CountDownLatch不可能重新初始化或者修改CountDownLatch對象的內(nèi)部計(jì)數(shù)器的值。一個(gè)線程調(diào)用countDown方法happens-before另一個(gè)線程調(diào)用的await()方法。
44. CyclicBarrier
讓一組線程達(dá)到一個(gè)屏障時(shí)被阻塞,知道最后一個(gè)線程到達(dá)屏障時(shí),屏障才會(huì)開門,所有被屏障攔截的線程才會(huì)繼續(xù)運(yùn)行。CyclicBarrier默認(rèn)的構(gòu)造方法是CyclicBarrier(int parties),其參數(shù)表示屏障攔截的線程數(shù)量,每個(gè)線程調(diào)用await方法告訴CyclicBarrier我已經(jīng)達(dá)到了屏障,然后當(dāng)前線程被阻塞。CyclicBarrier還提供了一個(gè)更高級的構(gòu)造函數(shù)CyclicBarrier(int parties, Runnable barrierAction)用于在線程達(dá)到屏障時(shí),優(yōu)先執(zhí)行barrierAction,方便處理更復(fù)雜的業(yè)務(wù)場景,舉例如下。
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;public class CyclicBarrierTest {static CyclicBarrier c = new CyclicBarrier(2,new A());public static void main(String[] args){new Thread(new Runnable(){@Overridepublic void run(){try{System.out.println(1);c.await();}catch (InterruptedException | BrokenBarrierException e){e.printStackTrace();}System.out.println(2);}}).start();try{System.out.println(3);c.await();}catch (InterruptedException | BrokenBarrierException e){e.printStackTrace();}System.out.println(4);}static class A implements Runnable{@Overridepublic void run(){System.out.println(5);}} }輸出結(jié)果:3 1 5 2 4
45. CyclicBarrier和CountDownLatch的區(qū)別
CountDownLatch的計(jì)數(shù)器只能使用一次,而CyclicBarrier的計(jì)數(shù)器可以使用reset()方法重置。
46. Semaphore
Semaphore(信號量)是用來控制同事訪問特定資源的線程數(shù)量,它協(xié)調(diào)各個(gè)線程,以保證合理的使用公共資源。Semaphore有兩個(gè)構(gòu)造函數(shù):Semaphore(int permits)默認(rèn)是非公平的,Semaphore(int permits, boolean fair)可以設(shè)置為公平的。應(yīng)用案例如下:
public class SemaphoreTest {private static final int THREAD_COUNT=30;private static ExecutorService threadPool = Executors.newFixedThreadPool(30);private static Semaphore s = new Semaphore(10);public static void main(String[] args){for(int i=0;i<THREAD_COUNT;i++){final int a = i;threadPool.execute(new Runnable(){@Overridepublic void run(){try{s.acquire();System.out.println("do something...."+a);s.release();}catch (InterruptedException e){e.printStackTrace();}}});}threadPool.shutdown();} }由上例可以看出Semaphore的用法非常的簡單,首先線程使用Semaphore的acquire()方法獲取一個(gè)許可證,使用完之后調(diào)用release()方法歸還許可證。還可以用tryAcquire()方法嘗試獲取許可證。Semaphore還提供了一些其他方法: int availablePermits()返回此信號量中當(dāng)前可用的許可證數(shù);int getQueueLength()返回正在等待獲取許可證的線程數(shù);boolean hasQueuedThreads()是否有線程正在等待獲取許可證;void reducePermits(int reduction)減少reduction個(gè)許可證,是個(gè)protected方法;Collection<Thread> getQueuedThreads()返回所有等待獲取許可證的線程集合,也是一個(gè)protected方法。
47. 線程間交換數(shù)據(jù)的Exchanger
Exchanger是一個(gè)用于線程間協(xié)作的工具類。Exchanger用于進(jìn)行線程間的數(shù)據(jù)交換。它提供一個(gè)同步點(diǎn),在這個(gè)同步點(diǎn),兩個(gè)線程可以交換彼此的數(shù)據(jù)。這兩個(gè)線程通過exchange方法交換數(shù)據(jù),如果第一個(gè)線程先執(zhí)行exchange()方法,它會(huì)一直等待第二個(gè)線程也執(zhí)行exchange方法。當(dāng)兩個(gè)線程都到達(dá)同步點(diǎn)時(shí),這兩個(gè)線程就可以交換數(shù)據(jù),將本現(xiàn)場生產(chǎn)出來的數(shù)據(jù)傳遞給對方。
import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ExchangerTest {private static final Exchanger<String> exchanger = new Exchanger<>();private static ExecutorService threadPool = Executors.newFixedThreadPool(2);public static void main(String[] args){threadPool.execute(new Runnable(){@Overridepublic void run(){String A = "I'm A!";try{String B = exchanger.exchange(A);System.out.println("In 1-"+Thread.currentThread().getName()+": "+B);}catch (InterruptedException e){e.printStackTrace();}}});threadPool.execute(new Runnable(){@Overridepublic void run(){try{String B="I'm B!";String A = exchanger.exchange(B);System.out.println("In 2-"+Thread.currentThread().getName()+": "+A);}catch (InterruptedException e){e.printStackTrace();}}});threadPool.shutdown();} }輸出結(jié)果:
In 2-pool-1-thread-2: I'm A! In 1-pool-1-thread-1: I'm B!如果兩個(gè)線程有一個(gè)沒有執(zhí)行exchange(V x)方法,則會(huì)一直等待,如果擔(dān)心有特殊情況發(fā)生,避免一直等待,可以使用exchange(V x, long timeout, TimeUnit unit)設(shè)置最大等待時(shí)長。
48. Java中的線程池ThreadPoolExecutor
可以通過ThreadPoolExecutor來創(chuàng)建一個(gè)線程池:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)- 如果運(yùn)行的線程數(shù)少于 corePoolSize,則 Executor 始終首選添加新的線程,而不進(jìn)行排隊(duì)。
- 如果運(yùn)行的線程數(shù)等于或多于 corePoolSize,則 Executor 始終首選將請求加入隊(duì)列,而不添加新的線程。
- 如果無法將請求加入隊(duì)列,則創(chuàng)建新的線程,除非創(chuàng)建此線程超出 maximumPoolSize,在這種情況下,任務(wù)將被拒絕。
- ThreadPoolExecutor.AbortPolicy:處理程序遭到拒絕,則直接拋出運(yùn)行時(shí)異常 RejectedExecutionException。(默認(rèn)策略)
- ThreadPoolExecutor.CallerRunsPolicy:調(diào)用者所在線程來運(yùn)行該任務(wù),此策略提供簡單的反饋控制機(jī)制,能夠減緩新任務(wù)的提交速度。
- ThreadPoolExecutor.DiscardPolicy:無法執(zhí)行的任務(wù)將被刪除。
- ThreadPoolExecutor.DiscardOldestPolicy:如果執(zhí)行程序尚未關(guān)閉,則位于工作隊(duì)列頭部的任務(wù)將被刪除,然后重新嘗試執(zhí)行任務(wù)(如果再次失敗,則重復(fù)此過程)。
可以使用兩個(gè)方法向線程池提交任務(wù),分別為execute()和submit()方法。execute()方法用于提交不需要返回值的任務(wù),所以無法判斷任務(wù)是否被線程池執(zhí)行成功。submit()方法用于提交需要返回值的任務(wù),線程池會(huì)返回一個(gè)Future類型的對象,通過這個(gè)對象可以判斷任務(wù)是否執(zhí)行成功。如Future<Object> future = executor.submit(task);
利用線程池提供的參數(shù)進(jìn)行監(jiān)控,參數(shù)如下:
- getTaskCount():線程池需要執(zhí)行的任務(wù)數(shù)量。
- getCompletedTaskCount():線程池在運(yùn)行過程中已完成的任務(wù)數(shù)量,小于或等于taskCount。
- getLargestPoolSize():線程池曾經(jīng)創(chuàng)建過的最大線程數(shù)量,通過這個(gè)數(shù)據(jù)可以知道線程池是否滿過。如等于線程池的最大大小,則表示線程池曾經(jīng)滿了。
- getPoolSize():線程池的線程數(shù)量。如果線程池不銷毀的話,池里的線程不會(huì)自動(dòng)銷毀,所以這個(gè)大小只增不減。
- getActiveCount():獲取活動(dòng)的線程數(shù)。
49. shutdown和shutdownNow
可以調(diào)用線程池的shutdown或者shutdownNow方法來關(guān)閉線程池。他們的原理是遍歷線程池的工作線程,然后逐個(gè)調(diào)用線程的interrupt方法來中斷線程,所以無法響應(yīng)中斷的任務(wù)可能永遠(yuǎn)無法停止。
區(qū)別:shutdown方法將執(zhí)行平緩的關(guān)閉過程:不在接收新的任務(wù),同時(shí)等待已提交的任務(wù)執(zhí)行完成——包括哪些還未開始執(zhí)行的任務(wù)。shutdownNow方法將執(zhí)行粗暴的關(guān)閉過程:它將嘗試取消所有運(yùn)行中的任務(wù),并且不再啟動(dòng)隊(duì)列中尚未開始執(zhí)行的任務(wù)。
只要調(diào)用了這兩個(gè)關(guān)閉方法中的任意一個(gè),isShutdown方法就會(huì)返回true,當(dāng)所有的任務(wù)都已關(guān)閉后,才表示線程池關(guān)閉成功,這時(shí)調(diào)用isTerminated方法會(huì)返回true。至于應(yīng)該調(diào)用哪一種方法來關(guān)閉線程池,應(yīng)該由提交到線程池的任務(wù)特性決定,通常調(diào)用shutdown方法來關(guān)閉線程池,如果任務(wù)不一定要執(zhí)行完,則可以調(diào)用shutdownNow方法。
50. 擴(kuò)展ThreadPoolExecutor
可以通過繼承線程池來自定義線程池,重寫線程池的beforeExecute, afterExecute和terminated方法。在執(zhí)行任務(wù)的線程中將調(diào)用beforeExecute和afterExecute等方法,在這些方法中還可以添加日志、計(jì)時(shí)、監(jiān)視或者統(tǒng)計(jì)信息收集的功能。無論任務(wù)是從run中正常返回,還是拋出一個(gè)異常而返回,afterExecute都會(huì)被調(diào)用。如果任務(wù)在完成后帶有一個(gè)Error,那么就不會(huì)調(diào)用afterExecute。如果beforeExecute拋出一個(gè)RuntimeException,那么任務(wù)將不被執(zhí)行,并且afterExecute也不會(huì)被調(diào)用。在線程池完成關(guān)閉時(shí)調(diào)用terminated,也就是在所有任務(wù)都已經(jīng)完成并且所有工作者線程也已經(jīng)關(guān)閉后,terminated可以用來釋放Executor在其生命周期里分配的各種資源,此外還可以執(zhí)行發(fā)送通知、記錄日志或者手機(jī)finalize統(tǒng)計(jì)等操作。詳細(xì)可以參考《JAVA多線程之?dāng)U展ThreadPoolExecutor》
持續(xù)更新中~
博主嘔心瀝血整理發(fā)布,跪求一贊。
wanna more?
Java多線程知識小抄集(一)
Java多線程知識小抄集(三)
參考資料
歡迎跳轉(zhuǎn)到本文的原文鏈接:https://honeypps.com/java/java-multiple-thread-summary-2/
歡迎支持筆者新作:《深入理解Kafka:核心設(shè)計(jì)與實(shí)踐原理》和《RabbitMQ實(shí)戰(zhàn)指南》,同時(shí)歡迎關(guān)注筆者的微信公眾號:朱小廝的博客。
總結(jié)
以上是生活随笔為你收集整理的Java多线程知识小抄集(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java多线程知识小抄集(一)
- 下一篇: Java多线程知识小抄集(三)