java 切换主线程_Java线程状态及切换、关闭线程的正确姿势分享
前言
在講線程之前有必要討論一下進(jìn)程的定義:進(jìn)程是程序在一個(gè)數(shù)據(jù)集合上運(yùn)行的過程,它是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。進(jìn)程實(shí)體由程序段, 數(shù)據(jù)段 PCB(進(jìn)程控制塊)組成。線程又是什么?線程可以看做輕量級(jí)進(jìn)程,線程是進(jìn)程的執(zhí)行單元,是進(jìn)程調(diào)度的基本單位
本文將詳細(xì)介紹關(guān)于Java線程狀態(tài)及切換、關(guān)閉線程的相關(guān)內(nèi)容,下面話不多說了,來一起看看詳細(xì)的介紹吧
1、線程狀態(tài)及切換
Java中的線程有六種狀態(tài),使用線程Thread內(nèi)的枚舉類來實(shí)現(xiàn),如下,我對(duì)每個(gè)狀態(tài)都進(jìn)行了一定的解釋。
public enum State {
/** 表示一個(gè)線程還沒啟用(即未調(diào)用start方法)*/
NEW,
/**
* JVM中執(zhí)行的線程都是處于這個(gè)狀態(tài)的,但是處于這個(gè)狀態(tài)不一定在JVM中執(zhí)行,
* 也就是說,只有這個(gè)狀態(tài)有資格被JVM調(diào)度從而獲得時(shí)間片執(zhí)行。
*/
RUNNABLE,
/**
* 線程在等待獲取鎖資源從而進(jìn)入阻塞狀態(tài),
* 在這個(gè)狀態(tài)中,其一直監(jiān)視鎖的動(dòng)態(tài),隨時(shí)準(zhǔn)備搶占鎖
* 若獲得鎖資源,重新進(jìn)入RUNNABLE狀態(tài)
*/
BLOCKED,
/**
* 當(dāng)調(diào)用Object.wait、Thread.join或者LockSupport類的park方法的時(shí)候,線程進(jìn)入此狀態(tài),
* 該狀態(tài)若無其他線程主動(dòng)喚醒,則無期限的等待。
* 喚醒的方法包括:Object.notify(喚醒隨機(jī)一個(gè))、Object.notifyAll(喚醒全部線程),
* 被喚醒的線程重新進(jìn)入RUNNABLE狀態(tài)
*/
WAITING,
/**
* 同WAITING狀態(tài),不過不同的是調(diào)用的方法加上了時(shí)間的限制,
* 例如:Object.wait(10)、Thread.sleep(10)、Thread.join(10)、LockSupport.parkNanos(10)、LockSupport.parkUntil(10)這些方法
* 喚醒的方法有兩種:
* 1、時(shí)間過期。
* 2、其他線程調(diào)用了notify或者notifyAll
* 喚醒之后同樣進(jìn)入RUNNABLE狀態(tài)
*/
TIMED_WAITING,
/** 線程的終點(diǎn)(正常死亡或者被終止)*/
TERMINATED;
}
除了NEW和TERMINATED之外,其他的狀態(tài)都是可以相互轉(zhuǎn)換的,其轉(zhuǎn)換過程如下圖所示
這里特別講一下RUNNABLE狀態(tài),在這個(gè)狀態(tài)中線程并不一定在執(zhí)行程序,只有被JVM調(diào)度的線程才能獲得執(zhí)行的時(shí)間片,并且只有這個(gè)狀態(tài)的線程才能夠獲得時(shí)間片,換句話說,被JVM調(diào)度并且獲得時(shí)間片是只屬于處于RUNNABLE狀態(tài)線程的權(quán)利。為了便于理解,可以將RUNNABLE分成Runnable和Running兩個(gè)狀態(tài)(當(dāng)然,你也可以換成其他的,這里我只是自己好理解),那么上面的線程轉(zhuǎn)換圖就轉(zhuǎn)變成了下面這樣(參考《Java并發(fā)編程的藝術(shù)》中的線程狀態(tài)圖):
關(guān)于線程狀態(tài)轉(zhuǎn)換的例子,可以通過下面的代碼加深理解
public class Test {
public static void main(String[] args) {
Test test = new Test();
// 1.NEW狀態(tài)
Thread thread = new Thread(() -> {
// 3.進(jìn)行test對(duì)象鎖的爭奪,若搶到鎖則繼續(xù)執(zhí)行,否則進(jìn)入BLOCKED狀態(tài)監(jiān)控該鎖,重新獲得后進(jìn)入RUNNABLE
synchronized (test) {
try {
// 4.進(jìn)入TIMED_WAITING狀態(tài),100ms后重新進(jìn)入RUNNABLE狀態(tài)爭奪時(shí)間片
Thread.sleep(100);
// 5.重新獲得時(shí)間片之后,進(jìn)入WAITING狀態(tài)
test.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 6.正常run()方法執(zhí)行完畢后線程結(jié)束,進(jìn)入TERMINATED
});
// 2.調(diào)用start()方法,線程進(jìn)入RUNNABLE狀態(tài)
thread.start();
}
}
注:代碼執(zhí)行的順序?yàn)樽⑨尩男蛱?hào)
2、正確的結(jié)束一個(gè)線程
在上面的例子中我們看到線程的run方法正常執(zhí)行完畢之后線程就正常死亡進(jìn)入TERMINATED狀態(tài)了,那么如果我們有中途停止線程的需求,我們應(yīng)該如何正確的結(jié)束一個(gè)線程呢?
使用interrupt()方法:在線程內(nèi)部,其定義了一個(gè)變量來標(biāo)識(shí)當(dāng)前線程是否處于被打斷狀態(tài),調(diào)用interrupt()方法則使這個(gè)狀態(tài)變?yōu)閠rue。我們采用這個(gè)方法加異常處理的方式來結(jié)束一個(gè)線程。
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
// 這里的return是必須的,原因后面說明
return;
}
System.err.println("thread interrupt test...");
});
thread.start();
thread.interrupt();
System.out.println("main thread end...");
}
// 結(jié)果圖:異常后面的語句不會(huì)打印
這里關(guān)于線程中的打斷標(biāo)識(shí)變量(之后以interrupt稱)需要說明的是,在特定的情況下其狀態(tài)會(huì)被重置。
1、線程內(nèi)部在catch了異常了之后interrupt的狀態(tài)會(huì)被重置為false。
2、線程調(diào)用了Thread.interrupted()方法之后,interrupt的狀態(tài)會(huì)被重置為false。如果需要判斷線程是否中斷的話可以使用對(duì)象方法isInterrupted(),此方法不會(huì)重置。
所以在剛才的代碼中需要加入return來結(jié)束線程,否則的話線程還是會(huì)繼續(xù)往下執(zhí)行,如下圖
使用isInterrupted()實(shí)現(xiàn):
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
int k = 0;
while (k++ < 10) {
System.out.println("do something..." + k);
}
}
System.err.println("thread end...");
});
thread.start();
Thread.sleep(1);
// 主線程流程執(zhí)行完了,需要停止線程
thread.interrupt();
}
使用標(biāo)識(shí)位來實(shí)現(xiàn):定義一個(gè)變量標(biāo)識(shí)線程是否終止,若終止了則退出run方法。跟上面isInterrupted()的實(shí)現(xiàn)一樣,不過換成了volatile變量而已。
public class Test {
public static volatile boolean interrupted = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (!interrupted) {
int k = 0;
while (k++ < 10) {
if (interrupted) {
System.err.println("thread invoke end....");
return;
}
System.out.println("do something..." + k);
}
}
System.err.println("thread end...");
});
thread.start();
Thread.sleep(1);
// 主線程流程執(zhí)行完了,需要停止線程
interrupted = true;
}
}
// 結(jié)果圖
stop()方法——不正確的線程中斷方法
在線程提供的方法中還有一個(gè)方法可以強(qiáng)制關(guān)閉線程——stop()。這個(gè)方法可以說是相當(dāng)?shù)陌缘?#xff0c;給人一種“我不管,我就是要你現(xiàn)在立刻死亡(指線程)”的感覺,并且其還會(huì)釋放線程所有的鎖資源,這樣可能會(huì)導(dǎo)致出現(xiàn)數(shù)據(jù)不一致從而出現(xiàn)線程不安全的情況,如下面例子。
public class Test {
public static volatile boolean flag = false;
public int state = 0;
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread = new Thread(() -> {
synchronized (test) {
try {
test.state = 1;
Thread.sleep(100);
if (flag) {
test.state = 2;
}
System.err.println("thread execute finished...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
Thread.sleep(1);
thread.stop();
flag = true;
System.out.println("state狀態(tài):" + test.state);
}
}
// 在這段代碼中,進(jìn)入線程時(shí)默認(rèn)將state賦為1,接著過一段時(shí)間后如果觸發(fā)了特定條件則把state賦為2,但是在特定條件觸發(fā)之前,線程就被終止掉了,這個(gè)特定條件雖然符合但卻沒辦法執(zhí)行,從而導(dǎo)致數(shù)據(jù)的不一致。
// 結(jié)果圖
所以,我們應(yīng)該采用上面兩種正確的方式而不是stop()來中止線程。此外,stop()方法若在線程start()之前執(zhí)行,那么在線程啟動(dòng)的時(shí)候就會(huì)立即死亡。
若有不對(duì)之處,望各位不吝指教(反正免費(fèi),對(duì)吧)。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
總結(jié)
以上是生活随笔為你收集整理的java 切换主线程_Java线程状态及切换、关闭线程的正确姿势分享的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python的进程线程和协程_pytho
- 下一篇: java 并发 set_高并发下的Jav