前言
繼續(xù)學(xué)習(xí)Java多線程基礎(chǔ)與使用詳細(xì)篇(一)----線程創(chuàng)建與線程停止下的知識(shí)企垦。
本篇會(huì)涉及多線程的生命周期、Thread和Object類中的重要方法詳解答恶、線程的各個(gè)屬性、
未捕獲異常如何處理具则。
一砾淌、線程的六個(gè)狀態(tài)(生命周期)
1.1 每個(gè)狀態(tài)的含義是
(1).NEW
新創(chuàng)建了一個(gè)線程對(duì)象啦撮,但還沒有調(diào)用start()方法
(2).Runnable
Java線程中將就緒(ready)和運(yùn)行中(running)兩種狀態(tài)籠統(tǒng)的稱為“運(yùn)行”。
線程對(duì)象創(chuàng)建后汪厨,其他線程(比如main線程)調(diào)用了該對(duì)象的start()方法赃春。該狀態(tài)的線程位于可運(yùn)行線程池中,等待被線程調(diào)度選中劫乱,獲取CPU的使用權(quán)织中,此時(shí)處于就緒狀態(tài)(ready)。就緒狀態(tài)的線程在獲得CPU時(shí)間片后變?yōu)檫\(yùn)行中狀態(tài)(running)衷戈。
(3).Blocked
表示線程阻塞于鎖狭吼。例如被同步代碼塊拿走了鎖后會(huì)被阻塞。
(4).Waiting
進(jìn)入該狀態(tài)的線程需要等待其他線程做出一些特定動(dòng)作(通知或中斷)脱惰。
(5).Timed Waiting
該狀態(tài)不同于WAITING搏嗡,它可以在指定的時(shí)間后自行返回。
(6).Terminated
終止(TERMINATED):表示該線程已經(jīng)執(zhí)行完畢拉一。
1.2 狀態(tài)間的轉(zhuǎn)化圖示
1.3 演示線程的NEW采盒、RUNNBALE、Terminated狀態(tài)
展示線程的NEW蔚润、RUNNABLE磅氨、Terminated狀態(tài)。即使是正在運(yùn)行嫡纠,也是Runnable狀態(tài)烦租,而不是Running。
public class NewRunnableTerminated implements Runnable {
public static void main(String[] args) {
Thread thread = new Thread(new NewRunnableTerminated());
//打印出NEW的狀態(tài)
System.out.println(thread.getState());
thread.start();
System.out.println(thread.getState());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印出RUNNABLE的狀態(tài)除盏,即使是正在運(yùn)行叉橱,也是RUNNABLE,而不是RUNNING
System.out.println(thread.getState());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印出TERMINATED狀態(tài)
System.out.println(thread.getState());
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
}
結(jié)果:
NEW
RUNNABLE
RUNNABLE
0
1
2
3
4
....
TERMINATED
1.4 展示Blocked者蠕,Waiting窃祝,TimedWaiting
public class BlockedWaitingTimedWaiting implements Runnable{
public static void main(String[] args) {
BlockedWaitingTimedWaiting runnable = new BlockedWaitingTimedWaiting();
Thread thread1 = new Thread(runnable);
thread1.start();
Thread thread2 = new Thread(runnable);
thread2.start();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印出Timed_Waiting狀態(tài),因?yàn)檎趫?zhí)行Thread.sleep(1000);
System.out.println(thread1.getState());
//打印出BLOCKED狀態(tài)踱侣,因?yàn)閠hread2想拿得到sync()的鎖卻拿不到
System.out.println(thread2.getState());
try {
Thread.sleep(1300);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印出WAITING狀態(tài)粪小,因?yàn)閳?zhí)行了wait()
System.out.println(thread1.getState());
}
@Override
public void run() {
syn();
}
private synchronized void syn() {
try {
Thread.sleep(1000);
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結(jié)果:
TIMED_WAITING
BLOCKED
WAITING
1.5 阻塞狀態(tài)是什么
(1).一般習(xí)慣而言,把Blocked(被阻塞)抡句、Waiting(等待)探膊、Timed_waiting(計(jì)時(shí)等待)都稱為阻塞狀態(tài)
(2). 不僅僅是Blocked
1.6. 線程生命周期總結(jié)
線程有六種狀態(tài),生命周期是什么待榔,這些都可以看上面的含義和畫圖看出來逞壁,或者通過網(wǎng)上學(xué)習(xí)更加深入的知識(shí)。
2. Thread和Object類中線程相關(guān)方法
2.1 方法概覽
2.1 wait,notify猾担,notifAll
作用袭灯、用法:
阻塞階段
直到以下4種情況之一發(fā)生時(shí)刺下,才會(huì)被喚醒 wait
(1). 另一個(gè)線程調(diào)用這個(gè)對(duì)象的notify()方法且剛好被喚醒的是本線程绑嘹;
(2).另一個(gè)線程調(diào)用這個(gè)對(duì)象的notifyAll()方法
(3).過了wait(long timeout)規(guī)定的超時(shí)時(shí)間,如果傳入0就永久等待
(4).線程自身調(diào)用了interrupt(),也會(huì)喚醒
喚醒階段:notify notifyAll
notify 一個(gè) 橘茉,隨機(jī)
notifyall 所有
遇到中斷
2.2 wait和notify的基本用法
普通用法:
主要是閱讀代碼的執(zhí)行順序工腋,了解wait是用來釋放鎖的,用一個(gè)代碼演示,從代碼可以看出線程一是用來釋放鎖畅卓,并睡眠2毫秒擅腰,這段實(shí)踐線程二剛好喚醒鎖線程一并獲取了鎖。
public class Wait {
public static Object object =
new Object();
//線程一
static class Thread1 extends Thread{
@Override
public void run() {
synchronized (object){
System.out.println(Thread.currentThread().getName()+"開始執(zhí)行了...");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程"+Thread.currentThread().getName()+
"獲取到了鎖翁潘。");
}
}
}
// 線程二
static class Thread2 extends Thread{
@Override
public void run() {
synchronized (object){
object.notify();
System.out.println("線程"+Thread.currentThread().getName()+"調(diào)用notify...");
}
}
}
public static void main(String args[]) throws InterruptedException {
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
Thread.sleep(200);
thread2.start();
}
}
Thread-0開始執(zhí)行了...
線程Thread-1調(diào)用notify...
線程Thread-0獲取到了鎖趁冈。
2.3 notify和notifyAll
使用3個(gè)線程,線程1和線程2首先被阻塞拜马,線程3喚醒它們渗勘。notify, notifyAll。 start先執(zhí)行不代表線程先啟動(dòng)俩莽。
notify 只會(huì)喚醒一條線程旺坠,于是導(dǎo)致第二條線程沒有喚醒一直永久的等待。
notifyAll 喚醒所有的扮超。
public class WaitNotifyAll implements Runnable {
private static final Object resourceA = new Object();
public static void main(String[] args) throws InterruptedException {
Runnable r = new WaitNotifyAll();
Thread threadA = new Thread(r);
Thread threadB = new Thread(r);
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA) {
resourceA.notifyAll();
// resourceA.notify();
System.out.println("ThreadC notified.");
}
}
});
threadA.start();
threadB.start();
// Thread.sleep(200);
threadC.start();
}
@Override
public void run() {
synchronized (resourceA) {
System.out.println(Thread.currentThread().getName()+" got resourceA lock.");
try {
System.out.println(Thread.currentThread().getName()+" waits to start.");
resourceA.wait();
System.out.println(Thread.currentThread().getName()+"'s waiting to end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
此時(shí)會(huì)獲取兩種結(jié)果
第一種是完美的執(zhí)行取刃,并不會(huì)去阻塞線程
Thread-0 got resourceA lock.
Thread-0 waits to start.
Thread-1 got resourceA lock.
Thread-1 waits to start.
ThreadC notified.
Thread-1's waiting to end.
Thread-0's waiting to end.
第二種是會(huì)出現(xiàn)阻塞現(xiàn)況
Thread-0 got resourceA lock.
Thread-0 waits to start.
ThreadC notified.
Thread-1 got resourceA lock.
Thread-1 waits to start.
Thread-0's waiting to end.
2.4 只釋放當(dāng)前monitor展示
證明wait只釋放當(dāng)前的那把鎖,只釋放當(dāng)前monitor展示出刷,每一個(gè)鎖都是一一對(duì)應(yīng)的關(guān)系璧疗。
下面是說 線程一會(huì)持有A,B對(duì)象,然后就釋放A馁龟,此時(shí)還持有B崩侠,接下來線程二是很順利的獲取A的資源,卻沒有獲取B的資源屁柏,因?yàn)锽的資源被線程一持有并沒有釋放
public class WaitNotifyReleaseOwnMonitor {
private static volatile Object resourceA = new Object();
private static volatile Object resourceB = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA) {
System.out.println("ThreadA got resourceA lock.");
synchronized (resourceB) {
System.out.println("ThreadA got resourceB lock.");
try {
System.out.println("ThreadA releases resourceA lock.");
resourceA.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resourceA) {
System.out.println("ThreadB got resourceA lock.");
System.out.println("ThreadB tries to resourceB lock.");
synchronized (resourceB) {
System.out.println("ThreadB got resourceB lock.");
}
}
}
});
thread1.start();
thread2.start();
}
}
2.5 wait啦膜、notify、notifyAll特點(diǎn)淌喻、性質(zhì)
1僧家、 必須先擁有monitor
2、 只能喚醒其中一個(gè)
3裸删、 屬于object類 ->所有對(duì)象的引入
4八拱、 類似功能的Condition wait、notify類似
5、 同時(shí)持有多個(gè)鎖的情況 -> 注意:釋放的順序->獲取的順序
2.6. 用wait/notify來實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式
EventStorage LinkedList表用來實(shí)現(xiàn)生產(chǎn)數(shù)據(jù)和存儲(chǔ)數(shù)據(jù)方法肌稻,通過生產(chǎn)者給消費(fèi)者消費(fèi)
public class ProducerConsumerModel {
public static void main(String[] args) {
EventStorage eventStorage = new EventStorage();
Producer producer = new Producer(eventStorage);
Consumer consumer = new Consumer(eventStorage);
new Thread(producer).start();
new Thread(consumer).start();
}
}
class Producer implements Runnable {
private EventStorage storage;
public Producer(
EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
storage.put();
}
}
}
class Consumer implements Runnable {
private EventStorage storage;
public Consumer(
EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
storage.take();
}
}
}
class EventStorage {
private int maxSize;
private LinkedList<Date> storage;
public EventStorage() {
maxSize = 10;
storage = new LinkedList<>();
}
public synchronized void put() {
while (storage.size() == maxSize) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
storage.add(new Date());
System.out.println("倉(cāng)庫(kù)里有了" + storage.size() + "個(gè)產(chǎn)品清蚀。");
notify();
}
public synchronized void take() {
while (storage.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("拿到了" + storage.poll() + ",現(xiàn)在倉(cāng)庫(kù)還剩下" + storage.size());
notify();
}
}
2.7 用wait和notify實(shí)現(xiàn)兩個(gè)線程交替打印0~100的奇偶數(shù)
由此發(fā)現(xiàn)為什么wait需要放在同步代碼塊中爹谭,是為了讓通信變得可靠枷邪、防止死鎖、等待的發(fā)生诺凡。如果沒有代碼塊的保護(hù)东揣,會(huì)隨時(shí)被notify切換過去。
而是sleep本身是針對(duì)自己腹泌,和其它線程關(guān)系并不大嘶卧。
public class WaitNotifyPrintOddEveWait {
private static int count = 0;
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(new TurningRunner(), "偶數(shù)").start();
new Thread(new TurningRunner(), "奇數(shù)").start();
}
//1. 拿到鎖,我們就打印
//2. 打印完凉袱,喚醒其他線程芥吟,自己就休眠
static class TurningRunner implements Runnable {
@Override
public void run() {
while (count <= 100) {
synchronized (lock) {
//拿到鎖就打印
System.out.println(Thread.currentThread().getName() + ":" + count++);
lock.notify();
if (count <= 100) {
try {
//如果任務(wù)還沒結(jié)束,就讓出當(dāng)前的鎖专甩,并休眠
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
2.8 為什么線程通信的方法wait(),notify()和notifyAll()被定義在Object類里钟鸵,而Sleep定義在Thread類里
1.notify()和notifyAll()是屬于一個(gè)對(duì)象的,事實(shí)上就是綁定到一個(gè)對(duì)象的鎖配深,
2.wait方法是屬于Object對(duì)象携添,那調(diào)用Thrad.wait本身就不適合
3.用supend()和resume()來阻塞線程可以嗎:不推薦,推薦使用wait /notify
3.sleep方法
作用 : 我只想讓現(xiàn)場(chǎng)在預(yù)期的時(shí)間執(zhí)行篓叶,其他時(shí)候不再占用CPU資源
3.1 sleep 方法不釋放鎖
包括 synchronized和lock, 和wait不同
使用synchronized代碼演示
展示線程sleep的時(shí)候不釋放synchronized的monitor烈掠,等sleep時(shí)間到了以后,正常結(jié)束后才釋放鎖
public class SleepDontReleaseMonitor implements Runnable {
public static void main(String[] args) {
SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
new Thread(sleepDontReleaseMonitor).start();
new Thread(sleepDontReleaseMonitor).start();
}
@Override
public void run() {
syn();
}
private synchronized void syn() {
System.out.println("線程" + Thread.currentThread().getName() + "獲取到了monitor缸托。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName() + "退出了同步代碼塊");
}
}
線程Thread-0獲取到了monitor左敌。
線程Thread-0退出了同步代碼塊
線程Thread-1獲取到了monitor。
線程Thread-1退出了同步代碼塊
使用lock代碼演示
sleep不釋放lock(lock需要手動(dòng)釋放)
public class SleepDontReleaseLock implements Runnable {
private static final Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
System.out.println("線程" + Thread.currentThread().getName() + "獲取到了鎖");
try {
Thread.sleep(5000);
System.out.println("線程" + Thread.currentThread().getName() + "已經(jīng)蘇醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
SleepDontReleaseLock sleepDontReleaseLock = new SleepDontReleaseLock();
new Thread(sleepDontReleaseLock).start();
new Thread(sleepDontReleaseLock).start();
}
}
線程Thread-0獲取到了鎖
線程Thread-0已經(jīng)蘇醒
線程Thread-1獲取到了鎖
線程Thread-1已經(jīng)蘇醒
3.2 sleep 方法響應(yīng)中斷
sleep方法可以讓線程進(jìn)入Waiting狀態(tài)俐镐,并且不占用CPU資源矫限,但是不釋放鎖,直到規(guī)定時(shí)間佩抹,后再執(zhí)行叼风,休眠期間如果被中斷,會(huì)拋出異常并清除中斷狀態(tài)棍苹。
Thread.sleep() 方法小于0會(huì)拋出異常
TimeUnit.SECONDS.sleep() 小于不會(huì)拋出異常无宿,推薦這種
public class SleepInterrupted implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new SleepInterrupted());
thread.start();
Thread.sleep(6500);
thread.interrupt();
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(new Date());
try {
TimeUnit.HOURS.sleep(3);
TimeUnit.MINUTES.sleep(25);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("我被中斷了!");
e.printStackTrace();
}
}
}
}
Sat Sep 05 10:20:43 CST 2020
我被中斷了枢里!
Sat Sep 05 10:20:50 CST 2020
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at threadcoreknowledge.threadobjectclasscommonmethods.SleepInterrupted.run(SleepInterrupted.java:51)
at java.lang.Thread.run(Thread.java:748)
3.3 sleep方法總結(jié)
wait/notify/sleep 異同(方法屬于哪個(gè)對(duì)象孽鸡,線程狀態(tài)怎么切換)
相同
阻塞
響應(yīng)中斷
不同
同步方法中
釋放鎖
指定時(shí)間
所屬類
4. join方法
作用: 因?yàn)樾碌木€程加入了我們蹂午,所以我們要等他執(zhí)行完再出發(fā)
用法:main等待thread1執(zhí)行完畢,注意誰(shuí)等誰(shuí)
4.1 普通用法代碼演示
演示join彬碱,注意語(yǔ)句輸出順序豆胸,會(huì)變化。
public class Join {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執(zhí)行完畢");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執(zhí)行完畢");
}
});
thread.start();
thread2.start();
System.out.println("開始等待子線程運(yùn)行完畢");
thread.join();
thread2.join();
System.out.println("所有子線程執(zhí)行完畢");
}
}
開始等待子線程運(yùn)行完畢
Thread-1執(zhí)行完畢
Thread-0執(zhí)行完畢
所有子線程執(zhí)行完畢
4.2 遇到中斷
演示join期間被中斷的效果
public class JoinInterrupt {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
mainThread.interrupt();
Thread.sleep(5000);
System.out.println("Thread1 finished.");
} catch (InterruptedException e) {
System.out.println("子線程中斷");
}
}
});
thread1.start();
System.out.println("等待子線程運(yùn)行完畢");
try {
thread1.join();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"主線程中斷了");
thread1.interrupt();
}
System.out.println("子線程已運(yùn)行完畢");
}
}
等待子線程運(yùn)行完畢
main主線程中斷了
子線程已運(yùn)行完畢
子線程中斷
4.3 在join期間巷疼,線程到底是什么狀態(tài)
Waiting
public class JoinThreadState {
public static void main(String[] args) throws InterruptedException {
Thread mainThread = Thread.currentThread();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println(mainThread.getState());
System.out.println("Thread-0運(yùn)行結(jié)束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
System.out.println("等待子線程運(yùn)行完畢");
thread.join();
System.out.println("子線程運(yùn)行完畢");
}
}
等待子線程運(yùn)行完畢
WAITING
Thread-0運(yùn)行結(jié)束
子線程運(yùn)行完畢
4.4 Join 原理
通過源碼可以看到Join內(nèi)部是有去調(diào)用wait方法
public final void join() throws InterruptedException {
join(0);
}
一開始是0晚胡,休眠時(shí)間是無(wú)線,直到喚醒或者中斷或者前面線程執(zhí)行完畢皮迟,
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
5. yield方法
作用:釋放CPU時(shí)間片
定位:JVM不保證遵循
yield和sleep區(qū)別:是否隨時(shí)可能再次被調(diào)度
6.獲取當(dāng)前執(zhí)行線程的引用:Thread.currentThread
演示打印main, Thread-0, Thread-1
public class CurrentThread implements Runnable {
public static void main(String[] args) {
new CurrentThread().run();
new Thread(new CurrentThread()).start();
new Thread(new CurrentThread()).start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
main
Thread-0
Thread-1
7.線程的各屬性總結(jié)
7.1 演示id代碼
ID從1開始搬泥,JVM運(yùn)行起來后,我們自己創(chuàng)建的線程的ID早已不是2
public class Id {
public static void main(String[] args) {
Thread thread = new Thread();
System.out.println("主線程的ID"+Thread.currentThread().getId());
System.out.println("子線程的ID"+thread.getId());
}
}
7.2 守護(hù)線程
1.作用:給用戶線程提供服務(wù)
2.3個(gè)特性:
1. 線程類型默認(rèn)繼承自父線程
2. 被誰(shuí)啟動(dòng)
3. 不影響退出JVM
3.守護(hù)線程和普通線程的區(qū)別
1. 整體上無(wú)區(qū)別
2. 唯一區(qū)別在于JVM 的離開
7.3 守護(hù)線程和普通線程的區(qū)別總結(jié)
1.他整體沒什么區(qū)別伏尼,區(qū)別是否會(huì)影響退出,一個(gè)為邏輯的尉尾,一個(gè)為服務(wù)我們的
2.我們是否需要給線程設(shè)置為守護(hù)線程
我們不應(yīng)該把線程設(shè)置成守護(hù)線程爆阶,會(huì)導(dǎo)致正在訪問文件期間,
就會(huì)關(guān)掉沙咏,操作的過程會(huì)強(qiáng)行的終止辨图。
開發(fā)過程中沒必要去設(shè)置。
7.4 線程優(yōu)先級(jí)
1.10個(gè)級(jí)別肢藐,默認(rèn)5
2.程序設(shè)計(jì)不應(yīng)依賴于優(yōu)先級(jí)
3. 不同操作系統(tǒng)不一樣
4. 優(yōu)先級(jí)會(huì)被操作系統(tǒng)改變
8. 未捕獲異常如何處理
8.1 為什么需要UncaughtExceptionHandler
(1).主線程可以輕松發(fā)現(xiàn)異常故河,子線程卻不行 ExceptionInChildThread######
此代碼演示,子線程會(huì)出現(xiàn)異常,但是主線程還是會(huì)繼續(xù)執(zhí)行下去
public class ExceptionInChildThread implements Runnable {
public static void main(String[] args) {
new Thread(new ExceptionInChildThread()).start();
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
@Override
public void run() {
throw new RuntimeException();
}
}
(2).子線程異常無(wú)法用傳統(tǒng)方法捕獲
例如一下代碼:
- 不加try catch拋出4個(gè)異常,都帶線程名字
- 加了try catch,期望捕獲到第一個(gè)線程的異常吆豹,線程234不應(yīng)該運(yùn)行鱼的,希望看到打印出Caught Exception
- 執(zhí)行時(shí)發(fā)現(xiàn),根本沒有Caught Exception痘煤,線程234依然運(yùn)行并且拋出異常
這樣說明不能直接捕獲的后果凑阶。
下面是使用了手動(dòng):在每個(gè)run方法里進(jìn)行try catch
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
try {
new Thread(new CantCatchDirectly(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-4").start();
} catch (RuntimeException e) {
System.out.println("Caught Exception.");
}
}
@Override
public void run() {
try {
throw new RuntimeException();
} catch (RuntimeException e) {
System.out.println("Caught Exception.");
}
}
}
Caught Exception.
Caught Exception.
Caught Exception.
Caught Exception.
8.2 利用UncaughtExceptionHandler接口(推薦)
1. UncaughtExceptionHandler接口
2. void uncaughtException(Thread t, Throwable e)######
3. 異常處理器的調(diào)用策略
代碼示例
public void uncaughtException(Thread t, Throwable e) {
// 默認(rèn)情況下parent是null
if (parent != null) {
parent.uncaughtException(t, e);
} else {
// 調(diào)用Thread.setDefaultUncaughtExceptionHandler();
// 方法設(shè)置的全局handler進(jìn)行處理
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
// 全局 handler 也不存在就輸出異常棧
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
4.實(shí)現(xiàn)自己的UncaughtExceptionHandler
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private String name;
public MyUncaughtExceptionHandler(String name) {
this.name = name;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.WARNING, "線程異常,終止啦" + t.getName());
System.out.println(name + "捕獲了異常" + t.getName() + "異常");
}
}
5. 調(diào)用上面UncaughtExceptionHandler
public class UseOwnUncaughtExceptionHandler implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("捕獲器1"));
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-4").start();
}
@Override
public void run() {
throw new RuntimeException();
}
}
警告: 線程異常衷快,終止啦MyThread-1
捕獲器1捕獲了異常MyThread-1異常
九月 05, 2020 3:16:38 下午 threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 線程異常宙橱,終止啦MyThread-2
捕獲器1捕獲了異常MyThread-2異常
捕獲器1捕獲了異常MyThread-3異常
九月 05, 2020 3:16:38 下午 threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 線程異常,終止啦MyThread-3
九月 05, 2020 3:16:39 下午 threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 線程異常蘸拔,終止啦MyThread-4
捕獲器1捕獲了異常MyThread-4異常
8.4 線程未捕獲異呈χ#總結(jié)
1.Java 異常體系
2.實(shí)際工作中,如何全家處理異常调窍? 為什么要全局護(hù)理宝冕?不處理行不行
用一個(gè)全局的UncaughtExceptionHandler,我們就可以根據(jù)自己的業(yè)務(wù)處理和前端和后端日志處理
3.run方法是否可以拋出異常陨晶?如果拋出異常猬仁,線程狀態(tài)會(huì)怎樣
如果提前沒有 catch異常帝璧,就會(huì)拋出異常,線程就會(huì)終止
4. 線程中如何處理某個(gè)未處理異常湿刽?
用一個(gè)全局處理器
9.總結(jié)
大致上就把Java 多線程的八大核心后面的四個(gè)學(xué)習(xí)了解的烁,這是用看某學(xué)習(xí)視頻總結(jié)而來的個(gè)人學(xué)習(xí)文章。希望自己也能對(duì)Java多線基礎(chǔ)鞏固起來诈闺。