本文分享自華為云社區《對Java多執行緒的用法感到一頭亂麻?40個問題讓你快速掌握多執行緒的精髓》,原文作者:breakDraw ,
多執行緒可以理解為在同一個程式中能夠同時運行多個不同的執行緒來執行不同的任務,這些執行緒可以同時利用CPU的多個核心運行,多執行緒編程能夠最大限度的利用CPU的資源,本文將通過以下幾個方向為大家講解多執行緒的用法,
- 1.Thread類基礎
- 2.synchronized關鍵字
- 3.其他的同步工具
- CountDownLatch
- FutureTask
- Semaphore
- CyclicBarrier
- Exchanger
- 原子類AtomicXXX
- 4.執行緒池
- 5.Thread狀態轉換
- 6.Volatile
- 7.執行緒群組
一、Thread類基礎
Q: Thread的deprecated過期方法是哪3個?作用是啥
A:
- stop(), 終止執行緒的執行,
- suspend(), 暫停執行緒執行,
- resume(), 恢復執行緒執行,
Q: 廢棄stop的原因是啥?
A:呼叫stop時,會直接終止執行緒并釋放執行緒上已鎖定的鎖,執行緒內部無法感知,并且不會做執行緒內的catch操作!即執行緒內部不會處理stop后的爛攤子,如果其他執行緒等在等著上面的鎖去取資料, 那么拿到的可能是1個半成品,
變成題目的話應該是下面這樣,問會輸出什么?
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println("start");
Thread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.stop();
// thread.interrupt();
}
}
class MyThread extends Thread {
public void run() {
try {
System.out.println("run");
Thread.sleep(5000);
} catch (Exception e) {
//處理爛攤子,清理資源
System.out.println("clear resource!");
}
}
}
答案是輸出 start和run,但是不會輸出clear resource
Q: stop的替代方法是什么?
A: interrupt(),
呼叫thread.interrupt()終止時, 不會直接釋放鎖,可通過呼叫interrupt()或者捕捉sleep產生的中斷例外,來判斷是否被終止,并處理爛攤子,
上題把thread.stop()改成thread.interrupt(),在Thread.sleep()程序中就會拋出interrupException(注意,InterrupExcetpion是sleep拋出的)因此就會輸出clear resource,如果沒有做sleep操作, 可以用isInterrupted()來判斷自己這個執行緒是否被終止了,來做清理,
另外注意一下interrupt和isInterrupted的區別:

Q: suspend/resume的廢棄原因是什么?
A: :呼叫suspend不會釋放鎖,
如果執行緒A暫停后,他的resume是由執行緒B來呼叫的,但是執行緒B又依賴A里的某個鎖,那么就死鎖了,例如下面這個例子,就要知道會引發死鎖:
public class Test {
public static Object lockObject = new Object();
public static void main(String[] args) throws InterruptedException {
System.out.println("start");
Thread thread = new MyThread();
thread.start();
Thread.sleep(1000);
System.out.println("主執行緒試圖占用lockObject鎖資源");
synchronized (Test.lockObject) {
// 用Test.lockObject做一些事
System.out.println("做一些事");
}
System.out.println("恢復");
thread.resume();
}
}
class MyThread extends Thread {
public void run() {
try {
synchronized (Test.lockObject) {
System.out.println("占用Test.lockObject");
suspend();
}
System.out.println("MyThread釋放TestlockObject鎖資源");
}
catch (Exception e){}
}
}
答案輸出

MyThread內部暫停后,外部的main因為沒法拿到鎖,所以無法執行后面的resume操作,
Q: 上題的suspend和resume可以怎么替換,來解決死鎖問題?
A: 可以用wait和noitfy來處理(不過盡量不要這樣設計,一般都是用run內部帶1個while回圈的)
public class Test {
public static Object lockObject = new Object(); //拿來做臨時鎖物件
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
Thread.sleep(1000);
System.out.println("主執行緒試圖占用lockObject鎖資源");
synchronized (Test.lockObject) {
// 用Test.lockObject做一些事
System.out.println("做一些事");
}
System.out.println("恢復");
synchronized (Test.lockObject) {
Test.lockObject.notify();
}
}
}
class MyThread extends Thread {
public void run() {
try {
synchronized (Test.lockObject) {
System.out.println("占用Test.lockObject");
Test.lockObject.wait();
}
System.out.println("MyThread釋放TestlockObject鎖資源");
}
catch (Exception e){}
}
}
如此執行,結果正常:

Q: 下面這例子為什么會運行例外,拋出IllegalMonitorStateException錯誤?
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
thread.notify();
}
A: notify和wait的使用前提是必須持有這個物件的鎖, 即main代碼塊 需要先持有thread物件的鎖,才能使用notify去喚醒(wait同理),
改成下面就行了:
Thread thread = new MyThread();
thread.start();
synchronized (thread) {
thread.notify();
}
Q: Thread.sleep()和Object.wait()的區別
A:sleep不會釋放物件鎖, 而wait會釋放物件鎖,
Q:Runnable介面和Callable的區別,
A: Callable可以和Futrue配合,并且啟動執行緒時用的時call,能夠拿到執行緒結束后的回傳值,call方法還能拋出例外,
Q:thread.alive()表示執行緒當前是否處于活躍/可用狀態,
活躍狀態: 執行緒已經啟動且尚未終止,執行緒處于正在運行或準備開始運行的狀態,就認為執行緒是“存活的
thread.start()后,是否alive()一定回傳true?
public class Main {
public static void main(String[] args) {
TestThread tt = new TestThread();
System.out.println("Begin == " + tt.isAlive());
tt.start();
System.out.println("end == " + tt.isAlive());
}
}
A:不一定,有可能在列印時,執行緒已經運行結束了,或者start后,還未真正啟動起來(就是還沒進入到run中)
Q: 執行緒A如下:
public class A extends Thread {
@Override
public void run() {
System.out.println("this.isAlive()=" + this.isAlive());
}
}
把執行緒A作為構造引數,傳給執行緒B
A a = new A();
Thread b = new Thread(a);
b.start()
此時會列印什么?
A:此時會列印false!

因為把a作為構造引數傳入b中, b執行start時, 實際上是在B執行緒中去呼叫了 A物件的run方法,而不是啟用了A執行緒,
如果改成
A a = new A();
a.start()
那么就會列印true了
Q:把FutureTask放進Thread中,并start后,會正常執行callable里的內容嗎?
public static void main(String[] args) throws Exception {
Callable<Integer> callable = () -> {
System.out.println("call 100");
return 100;
};
FutureTask<Integer> task = new FutureTask<>(callable);
Thread thread = new Thread(task);
thread.start();
}
A:能正常列印
二、synchronized關鍵字
- 即可作為方法的修飾符,也可以作為代碼塊的修飾符
- 注意修飾方法時,并不是這個方法上有鎖, 而是呼叫該方法時,需要取該方法所在物件上的鎖,
class A{
synchroized f(){
}
}
即呼叫這個f(), 并不是說f同一時刻只能進入一次,而是說進入f時,需要取到A上的鎖,
Q: 呼叫下面的f()時,會出現死鎖嗎?
class A{
synchroized f(){
t()
}
synchroized t(){
}
}
A:不會,
1個執行緒內, 可以重復進入1個物件的synchroized 塊,
- 原理:
當執行緒請求自己的鎖時,JVM會記下鎖的持有者,并且給這個鎖計數為1,如果該執行緒再次請求自己的鎖,則可以再次進入,計數為2,退出時計數-1,直到全部退出時才會釋放鎖,
Q:2個執行緒同時呼叫f1和f2會產生同步嗎?
class A{
private static synchronized void f1(){};
private synchronized void f2(){};
}
A:不會產生同步,二者不是1個鎖,
f1是類鎖,等同于synchronized(A.class)
f2是物件鎖,
三、其他的同步工具
CountDownLatch
final CountDownLatch latch = new CountDownLatch(2);
2是計數器初始值,
然后執行latch.await()時, 就會阻塞,直到其他執行緒中把這個latch進行latch.countDown(),并且計數器降低至0,
- 和join的區別:
join阻塞時,是只等待單個執行緒的完成
而CountDownLatch可能是為了等待多個執行緒
Q: countDownLatch的內部計數值能被重置嗎?
A:不能重置了,如果要重新計數必須重新new一個,畢竟他的類名就叫DownLatch
FutureTask
可以理解為一個支持有回傳值的執行緒
FutureTask<Integer> task = new FutureTask<>(runable);
當呼叫task.get()時,就能能達到執行緒里的回傳值
Q:呼叫futrueTask.get()時,這個是阻塞方法嗎?如果是阻塞,什么時候會結束?
A:是阻塞方法,
- 執行緒跑完并回傳結果
- 阻塞時間達到futrueTask.get(xxx)里設定的xxx時間
- 執行緒出現例外InterruptedException或者ExecutionException
- 執行緒被取消,拋出CancellationException
Semaphore
信號量:就是作業系統里常見的那個概念,java實作,用于各執行緒間進行資源協調,
用Semaphore(permits)構造一個包含permits個資源的信號量,然后某執行緒做了消費動作, 則執行semaphore.acquire(),則會消費一個資源,如果某執行緒做了生產動作,則執行semaphore.release(),則會釋放一個資源(即新增一個資源)
更詳細的信號量方法說明:https://blog.csdn.net/hanchao5272/article/details/79780045
Q: 信號量中,公平模式和非公平模式的區別?下面設成true就是公平模式
//new Semaphore(permits,fair):初始化許可證數量和是否公平模式的建構式
semaphore = new Semaphore(5, true);
A:其實就是使用哪種公平鎖還是非公平鎖,

Java并發中的fairSync和NonfairSync主要區別為:
- 如果當前執行緒不是鎖的占有者,則NonfairSync并不判斷是否有等待佇列,直接使用compareAndSwap去進行鎖的占用,即誰正好搶到,就給誰用!
- 如果當前執行緒不是鎖的占有者,則FairSync則會判斷當前是否有等待佇列,如果有則將自己加到等待佇列尾,即嚴格的先到先得!
CyclicBarrier
柵欄,一般是在執行緒中去呼叫的,它的構造需要指定1個執行緒數量,和柵欄被破壞前要執行的操作,每當有1個執行緒呼叫barrier.await(),就會進入阻塞,同時barrier里的執行緒計數-1,
當執行緒計數為0時, 呼叫柵欄里指定的那個操作后,然后破壞柵欄, 所有被阻塞在await上的執行緒繼續往下走,
Exchanger
我理解為兩方柵欄,用于交換資料,
簡單說就是一個執行緒在完成一定的事務后,想與另一個執行緒交換資料,則第一個先拿出資料的執行緒會一直等待第二個執行緒,直到第二個執行緒拿著資料到來時才能彼此交換對應資料,
原子類AtomicXXX
就是內部已實作了原子同步機制
Q:下面輸出什么?(考察getAndAdd的用法)
AtomicInteger num = new AtomicInteger(1);
System.out.println(num.getAndAdd(1));
System.out.println(num.get());
A:輸出1、2
顧名思義, getAndAdd(),那么就是先get,再加, 類似于num++,
如果是addAndGet(),那么就是++num
Q:AtomicReference和AtomicInteger的區別?
A:AtomicInteger是對整數的封裝,而AtomicReference則對應普通的物件參考,也就是它可以保證你在修改物件參考時的執行緒安全性,即可能會有多個執行緒修改atomicReference里包含的參考,
- 經典用法:
boolean exchanged = atomicStringReference.compareAndSet(initialReference, newReference)就是經典的CAS同步法
compreAndSet它會將將參考與預期值(參考)進行比較,如果它們相等,則在AtomicReference物件內設定一個新的參考,類似于一個非負責的自旋鎖, - AtomicReferenceArray是原子陣列, 可以進行一些原子的陣列操作例如 set(index, value),
java中已實作的全部原子類:

注意,沒有float,沒有short和byte,
四、執行緒池
Q: ThreadPoolExecutor執行緒池構造引數中,corePoolSize和maximumPoolSize有什么區別?
A:當提交新執行緒到池中時
- 如果當前執行緒數 < corePoolSize,則會創建新執行緒
- 如果當前執行緒數=corePoolSize,則新執行緒被塞進一個佇列中等待,
- 如果佇列也被塞滿了,那么又會開始新建執行緒來運行任務,避免任務阻塞或者丟棄
- 如果佇列滿了的情況下, 執行緒總數超過了maxinumPoolSize,那么就拋例外或者阻塞(取決于佇列性質),
- 呼叫prestartCoreThread()可提前開啟一個空閑的核心執行緒
- 呼叫prestartAllCoreThreads(),可提前創建corePoolSize個核心執行緒,
Q: 執行緒池的keepalive引數是干嘛的?
A:當執行緒數量在corePoolSize到maxinumPoolSize之間時, 如果有執行緒已跑完,且空閑時間超過keepalive時,則會被清除(注意只限于corePoolSize到maxinumPoolsize之間的執行緒)
Q: 執行緒池有哪三種佇列策略?
A:
- 握手佇列
相當于不排隊的佇列,可能造成執行緒數量無限增長直到超過maxinumPoolSize(相當于corePoolSize沒什么用了,只以maxinumPoolSize做上限) - 無界佇列
佇列隊長無限,即執行緒數量達到corePoolSize時,后面的執行緒只會在佇列中等待,(相當于maxinumPoolSize沒什么用了)
缺陷: 可能造成佇列無限增長以至于OOM - 有界佇列
Q: 執行緒池佇列已滿且maxinumPoolSize已滿時,有哪些拒絕策略?
A:
- AbortPolicy 默認策略:直接拋出RejectedExecutionException例外
- DiscardPolicy 丟棄策略: 直接丟了,什么錯誤也不報
- DiscardOldestPolicy 丟棄隊頭策略: 即把最先入隊的人從隊頭扔出去,再嘗試讓該任務進入隊尾(隊頭任務內心:不公平,,,,)
- CallerRunsPolicy 呼叫者處理策略: 交給呼叫者所在執行緒自己去跑任務(即誰呼叫的submit或者execute,他就自己去跑)
- 也可以用實作自定義新的RejectedExecutionHandler
Q:有以下五種Executor提供的執行緒池,注意記憶一下他們的用途,就能理解內部的原理了,
- newCachedThreadPool: 快取執行緒池
corePoolSize=0, maxinumPoolSize=+∞,佇列長度=0 ,因此執行緒數量會在corePoolSize到maxinumPoolSize之間一直靈活快取和變動, 且不存在佇列等待的情況,一來任務我就創建,用完了會釋放,

- newFixedThreadPool :定長執行緒池
corePoolSize= maxinumPoolSize=構造引數值, 佇列長度=+∞,因此不存在執行緒不夠時擴充的情況 - newScheduledThreadPool :定時器執行緒池
提交定時任務用的,構造引數里會帶定時器的間隔和單位, 其他和FixedThreadPool相同,屬于定長執行緒池, - newSingleThreadExecutor : 單執行緒池
corePoolSize=maxinumPoolSize=1, 佇列長度=+∞,只會跑一個任務, 所以其他的任務都會在佇列中等待,因此會嚴格按照FIFO執行 - newWorkStealingPool(繼承自ForkJoinPool ): 并行執行緒池
如果你的任務執行時間很長,并且里面的任務運行并行跑的,那么他會把你的執行緒任務再細分到其他的執行緒來分治,ForkJoinPool介紹:https://blog.csdn.net/m0_37542889/article/details/92640903
Q: submit和execute的區別是什么?
A:
- execute只能接收Runnable型別的任務,而submit除了Runnable,還能接收Callable(Callable型別任務支持回傳值)
- execute方法回傳void, submit方法回傳FutureTask,
- 例外方面, submit方法因為回傳了futureTask物件,而當進行future.get()時,會把執行緒中的例外拋出,因此呼叫者可以方便地處理例外,(如果是execute,只能用內部捕捉或者設定catchHandler)
Q:執行緒池中, shutdown、 shutdownNow、awaitTermination的區別?
A:
- shutdown: 停止接收新任務,等待所有池中已存在任務完成( 包括等待佇列中的執行緒 ),異步方法,即呼叫后馬上返回,
- shutdownNow: 停止接收新任務,并 停止所有正執行的task,回傳還在佇列中的task串列 ,
- awaitTermination: 僅僅是一個判斷方法,判斷當前執行緒池任務是否全部結束,一般用在shutdown后面,因為shutdown是異步方法,你需要知道什么時候才真正結束,
五、Thread狀態轉換
Q: 執行緒的6種狀態是:
A:
- New: 新建了執行緒,但是還沒呼叫start
- RUNNABLE: 運行, 就緒狀態包括在運行態中
- BLOCKED: 阻塞,一般是因為想拿鎖拿不到
- WAITING: 等待,一般是wait或者join之后
- TIMED_WAITING: 定時等待,即固定時間后可回傳,一般是呼叫sleep或者wait(時間)的,
- TERMINATED: 終止狀態,
欣賞一幅好圖,能了解呼叫哪些方法會進入哪些狀態,

原圖鏈接
Q: java執行緒什么時候會進入阻塞(可能按多選題考):
A:
- sleep
- wati()掛起, 等待獲得別的執行緒發送的Notify()訊息
- 等待IO
- 等待鎖
六、Volatile
用volatile修飾成員變數時, 一旦有執行緒修改了變數,其他執行緒可立即看到改變,
Q: 不用volatile修飾成員變數時, 為什么其他執行緒會無法立即看到改變?
A:執行緒可以把變數保存在本地記憶體(比如機器的暫存器)中,而不是直接在主存中進行讀寫,
這就可能造成一個執行緒在主存中修改了一個變數的值,而另外一個執行緒還繼續使用它在暫存器中的變數值,
Q: 用了volatile是不是就可以不用加鎖啦?
A: 不行,
- 鎖并不是只保證1個變數的互斥, 有時候是要保證幾個成員在連續變化時,讓其他執行緒無法干擾、讀取,
- 而volatile保證1個變數可變, 保證不了幾個變數同時變化時的原子性,
Q:展示一段《Java并發編程實戰》書里的一個經典例子,在科目二考試里也出現了,只是例子換了個皮,為什么下面這個例子可能會死回圈,或者輸出0?

A:首先理解一下java重排序,可以看一下這篇博文:https://www.cnblogs.com/coshaho/p/8093944.html
然后分析后面那2個奇怪的情況是怎么發生的,
- 永遠不輸出:
經程序式的指令排序,出現了這種情況:
- ReaderThread在while里讀取ready值, 此時是false, 于是存入了ReaderThread的暫存器,
- 主執行緒修改ready和number,
- ReaderThread沒有感知到ready的修改(對于ReaderThread執行緒,感知不到相關的指令,來讓他更新ready暫存器的值),因此進入死回圈,
- 輸出0
經程序式的指令排序,出現了這種情況:
1)主執行緒設定ready為true
2)ReaderThread在while里讀取ready值,是true,于是退出while回圈
- ReaderThread讀取到number值, 此時number還是初始化的值為0,于是輸出0
- 主執行緒這時候才修改number=42,此時ReaderThread已經結束了!
上面這個問題,可以用volatile或者加鎖,當你加了鎖時, 如果變數被寫了,會有指令去更新另一個暫存器的值,因此就可見了,
七、執行緒群組
為了方便管理一批執行緒,我們使用ThreadGroup來表示執行緒組,通過它對一批執行緒進行分類管理
使用方法:
Thread group = new ThreadGroup("group");
Thread thread = new Thread(gourp, ()->{..});
即thread除了Thread(Runable)這個構造方法外,還有個Thread(ThreadGroup, Runnable)構造方法
Q:在執行緒A中創建執行緒B, 他們屬于同一個執行緒組嗎
A:是的
執行緒組的一大作用是對同一個組執行緒進行統一的例外捕捉處理,避免每次新建執行緒時都要重新去setUncaghtExceptionHandler,即執行緒組自身可以實作一個uncaughtException方法,
ThreadGroup group = new ThreadGroup("group") {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.println(thread.getName() + throwable.getMessage());
}
};
}
執行緒如果拋出例外,且沒有在執行緒內部被捕捉,那么此時執行緒例外的處理順序是什么?相信很多人都看過下面這段話,好多講執行緒組的博客里都這樣寫:
(1)首先看看當前執行緒組(ThreadGroup)有沒有父類的執行緒組,如果有,則使用父類的UncaughtException()方法,
(2)如果沒有,就看執行緒是不是呼叫setUncaughtExceptionHandler()方法建立Thread.setUncaughtExceptionHandler實體,如果建立,直接使用它的UncaughtException()方法處理例外,
(3)如果上述都不成立就看這個例外是不是ThreadDead實體,如果是,什么都不做,如果不是,輸出堆疊追蹤資訊(printStackTrace),
來源:
https://blog.csdn.net/qq_43073128/article/details/90597006
https://blog.csdn.net/qq_43073128/article/details/88280469
好,別急著記,先看一下下面的題目,問輸出什么:
Q:
// 父類執行緒組
static class GroupFather extends ThreadGroup {
public GroupFather(String name) {
super(name);
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.println("groupFather=" + throwable.getMessage());
}
}
public static void main(String[] args) {
// 子類執行緒組
GroupFather groupSon = new GroupFather("groupSon") {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.println("groupSon=" + throwable.getMessage());
}
};
Thread thread1 = new Thread(groupSon, ()->{
throw new RuntimeException("我例外了");
});
thread1.start();
}
A:一看(1),那是不是應該輸出groupFather?
錯錯錯,輸出的是groupSon這句話在很多地方能看到,但沒有去實踐過看過原始碼的人就會這句話被誤導,實際上父執行緒組不是指類繼承關系上的執行緒組,而是指下面這樣的:

即指的是構造關系的有父子關系,如果子類的threadGroup沒有去實作uncaughtException方法,那么就會去構造引數里指定的父執行緒組去呼叫方法,
Q: 那我改成構造關系上的父子關系,下面輸出什么?
public static void main(String[] args) {
// 父執行緒組
ThreadGroup groupFather = new ThreadGroup("groupFather") {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.println("groupFather=" + throwable.getMessage());
}
};
// 子執行緒組,把groupFather作為parent引數
ThreadGroup groupSon = new ThreadGroup(groupFather, "groupSon") {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.println("groupSon=" + throwable.getMessage());
}
};
Thread thread1 = new Thread(groupSon, ()->{
throw new RuntimeException("我例外了");
});
thread1.start();
}
A:答案輸出

即只要子執行緒組有實作過,則會用子執行緒組里的方法,而不是直接去找的父執行緒組!
Q:如果我讓自己做set捕捉器的操作呢?那下面這個輸出什么?
public static void main(String[] args) {
// 父執行緒組
ThreadGroup group = new ThreadGroup("group") {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.println("group=" + throwable.getMessage());
}
};
// 建一個執行緒,在執行緒組內
Thread thread1 = new Thread(group, () -> {
throw new RuntimeException("我例外了");
});
// 自己設定setUncaughtExceptionHandler方法
thread1.setUncaughtExceptionHandler((t, e) -> {
System.out.println("no gourp:" + e.getMessage());
});
thread1.start();
}
A:看之前的結論里,似乎是應該輸出執行緒組的例外?
但是結果卻輸出的是:

也就是說,如果執行緒對自己特地執行過setUncaughtExceptionHandler,那么有優先對自己設定過的UncaughtExceptionHandler做處理,
那難道第(2)點這個是錯的嗎?確實錯了,實際上第二點應該指的是全域Thread的默認捕捉器,注意是全域的,實際上那段話出自ThreadGroup里uncaughtException的原始碼:

這里就解釋了之前的那三點,但是該代碼中沒考慮執行緒自身設定了捕捉器
所以修改一下之前的總結一下執行緒的實際例外拋出判斷邏輯:
- 如果執行緒自身有進行過setUncaughtExceptionHandler,則使用自己設定的按個,
- 如果沒設定過,則看一下沒有執行緒組,并按照以下邏輯判斷:
如果執行緒組有覆寫過uncaughtException,則用覆寫過的uncaughtException
如果執行緒組沒有覆寫過,則去找父執行緒組(注意是構造體上的概念)的uncaughtException方法, - 如果執行緒組以及父類都沒覆寫過uncaughtException, 則判斷是否用Thread.setDefaultUncaughtExceptionHandler(xxx)去設定全域的默認捕捉器,有的話則用全域默認
- 如果不是ThreadDeath執行緒, 則只列印堆疊,
- 如果是ThreadDeath執行緒,那么就什么也不處理,
點擊關注,第一時間了解華為云新鮮技術~
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/280732.html
標籤:其他
上一篇:字串與記憶體函式
