
1. 線程的創(chuàng)建
首先我們來(lái)復(fù)習(xí)我們學(xué)習(xí) java 時(shí)接觸的線程創(chuàng)建,這也是面試的時(shí)候喜歡問(wèn)的,有人說(shuō)兩種也有人說(shuō)三種四種等等,其實(shí)我們不能去死記硬背,而應(yīng)該深入理解其中的原理,當(dāng)我們理解后就會(huì)發(fā)現(xiàn)所謂的創(chuàng)建線程實(shí)質(zhì)都是一樣的,在我們面試的過(guò)程中如果我們能從本質(zhì)出發(fā)回答這樣的問(wèn)題,那么相信一定是個(gè)加分項(xiàng)!好了我們不多說(shuō)了,開(kāi)始今天的 code 之路<br />
<a name="jC5ZN"></a>
1.1 **繼承 Thread 類創(chuàng)建線程 **
**
- 這是我們最常見(jiàn)的創(chuàng)建線程的方式,通過(guò)繼承
Thread類來(lái)重寫run方法,
<br />代碼如下:
/**
* 線程類
* url: www.i-code.online
* @author: anonyStar
* @time: 2020/9/24 18:55
*/
public class ThreadDemo extends Thread {
@Override
public void run() {
//線程執(zhí)行內(nèi)容
while (true){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThredDemo 線程正在執(zhí)行,線程名:"+ Thread.currentThread().getName());
}
}
}
測(cè)試方法:
@Test
public void thread01(){
Thread thread = new ThreadDemo();
thread.setName("線程-1 ");
thread.start();
while (true){
System.out.println("這是main主線程:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結(jié)果:<br />
繼承
Thread的線程創(chuàng)建簡(jiǎn)單,啟動(dòng)時(shí)直接調(diào)用start方法,而不是直接調(diào)用run方法。直接調(diào)用run等于調(diào)用普通方法,并不是啟動(dòng)線程
<a name="OVK3X"></a>
1.2 **實(shí)現(xiàn) Runnable 接口創(chuàng)建線程 **
**
- 上述方式我們是通過(guò)繼承來(lái)實(shí)現(xiàn)的,那么在
java中提供了Runnable接口,我們可以直接實(shí)現(xiàn)該接口,實(shí)現(xiàn)其中的run方法,這種方式可擴(kuò)展性更高
<br />代碼如下:
/**
* url: www.i-code.online
* @author: anonyStar
* @time: 2020/9/24 18:55
*/
public class RunnableDemo implements Runnable {
@Override
public void run() {
//線程執(zhí)行內(nèi)容
while (true){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("RunnableDemo 線程正在執(zhí)行,線程名:"+ Thread.currentThread().getName());
}
}
}
測(cè)試代碼:
@Test
public void runnableTest(){
// 本質(zhì)還是 Thread ,這里直接 new Thread 類,傳入 Runnable 實(shí)現(xiàn)類
Thread thread = new Thread(new RunnableDemo(),"runnable子線程 - 1");
//啟動(dòng)線程
thread.start();
while (true){
System.out.println("這是main主線程:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運(yùn)行結(jié)果:<br />
<br />
<a name="uWp9R"></a>
1.3 實(shí)現(xiàn) Callable 接口創(chuàng)建線程
<br />
- 這種方式是通過(guò) 實(shí)現(xiàn)
Callable接口,實(shí)現(xiàn)其中的call方法來(lái)實(shí)現(xiàn)線程,但是這種線程創(chuàng)建的方式是依賴于 ****FutureTask **包裝器**來(lái)創(chuàng)建Thread, 具體來(lái)看代碼
<br />代碼如下:<br />
/**
* url: www.i-code.online
* @author: anonyStar
* @time: 2020/9/24 18:55
*/
public class CallableDemo implements Callable<String> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
@Override
public String call() throws Exception {
//線程執(zhí)行內(nèi)容
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CallableDemo 線程正在執(zhí)行,線程名:"+ Thread.currentThread().getName());
return "CallableDemo 執(zhí)行結(jié)束。。。。";
}
}
測(cè)試代碼:
@Test
public void callable() throws ExecutionException, InterruptedException {
//創(chuàng)建線程池
ExecutorService service = Executors.newFixedThreadPool(1);
//傳入Callable實(shí)現(xiàn)同時(shí)啟動(dòng)線程
Future submit = service.submit(new CallableDemo());
//獲取線程內(nèi)容的返回值,便于后續(xù)邏輯
System.out.println(submit.get());
//關(guān)閉線程池
service.shutdown();
//主線程
System.out.println("這是main主線程:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
結(jié)果:<br />
<br />
有的時(shí)候,我們可能需要讓一步執(zhí)行的線程在執(zhí)行完成以后,提供一個(gè)返回值給到當(dāng)前的主線程,主線程需要依賴這個(gè)值進(jìn)行后續(xù)的邏輯處理,那么這個(gè)時(shí)候,就需要用到帶返回值的線程了
<br />
<br />關(guān)于線程基礎(chǔ)知識(shí)的如果有什么問(wèn)題的可以在網(wǎng)上查找資料學(xué)習(xí)學(xué)習(xí)!這里不再闡述<br />
<a name="UJPNU"></a>
2. 線程的生命周期
- Java 線程既然能夠創(chuàng)建,那么也勢(shì)必會(huì)被銷毀,所以線程是存在生命周期的,那么我們接下來(lái)從線程的生命周期開(kāi)始去了解線程。
<a name="Ky5cq"></a>
2.1 線程的狀態(tài)
<a name="3uFrY"></a>
2.1.1 線程六狀態(tài)認(rèn)識(shí)
<br />線程一共有 6 種狀態(tài)(NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED)<br />
NEW:初始狀態(tài),線程被構(gòu)建,但是還沒(méi)有調(diào)用 start 方法
RUNNABLED:運(yùn)行狀態(tài),JAVA 線程把操作系統(tǒng)中的就緒和運(yùn)行兩種狀態(tài)統(tǒng)一稱為“運(yùn)行中”
-
BLOCKED:阻塞狀態(tài),表示線程進(jìn)入等待狀態(tài), 也就是線程因?yàn)槟撤N原因放棄了 CPU 使用權(quán),阻塞也分為幾種情況
- 等待阻塞:運(yùn)行的線程執(zhí)行 wait 方法,jvm 會(huì)把當(dāng)前線程放入到等待隊(duì)列? 同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被其他線程鎖占用了,那么 jvm 會(huì)把當(dāng)前的線程放入到鎖池中
- 其他阻塞:運(yùn)行的線程執(zhí)行 Thread.sleep 或者 t.join 方法,或者發(fā)出了 I/O 請(qǐng)求時(shí),JVM 會(huì)把當(dāng)前線程設(shè)置為阻塞狀態(tài),當(dāng) sleep 結(jié)束、join 線程終止、io 處理完畢則線程恢復(fù)
TIME_WAITING:超時(shí)等待狀態(tài),超時(shí)以后自動(dòng)返回
TERMINATED:終止?fàn)顟B(tài),表示當(dāng)前線程執(zhí)行完畢

<br />
<a name="gpksO"></a>
2.1.2 代碼實(shí)操演示
- 代碼:
public static void main(String[] args) {
////TIME_WAITING 通過(guò) sleep wait(time) 來(lái)進(jìn)入等待超時(shí)中
new Thread(() -> {
while (true){
//線程執(zhí)行內(nèi)容
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Time_Waiting").start();
//WAITING, 線程在 ThreadStatus 類鎖上通過(guò) wait 進(jìn)行等待
new Thread(() -> {
while (true){
synchronized (ThreadStatus.class){
try {
ThreadStatus.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"Thread_Waiting").start();
//synchronized 獲得鎖,則另一個(gè)進(jìn)入阻塞狀態(tài) blocked
new Thread(() -> {
while (true){
synchronized(Object.class){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"Object_blocked_1").start();
new Thread(() -> {
while (true){
synchronized(Object.class){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"Object_blocked_2").start();
}
啟動(dòng)一個(gè)線程前,最好為這個(gè)線程設(shè)置線程名稱,因?yàn)檫@樣在使用 jstack 分析程序或者進(jìn)行問(wèn)題排查時(shí),就會(huì)給開(kāi)發(fā)人員提供一些提示
<a name="fLqjA"></a>
2.1.3 線程的狀態(tài)堆棧
<br />? 運(yùn)行該示例,打開(kāi)終端或者命令提示符,鍵入“ jps ”, ( JDK1.5 提供的一個(gè)顯示當(dāng)前所有 java 進(jìn)程 pid 的命令) <br />
<br />? 根據(jù)上一步驟獲得的 pid ,繼續(xù)輸入 jstack pid (jstack是 java 虛擬機(jī)自帶的一種堆棧跟蹤工具。jstack 用于打印出給定的 java 進(jìn)程 ID 或 core file 或遠(yuǎn)程調(diào)試服務(wù)的 Java 堆棧信息)<br />

<a name="YxkTb"></a>
3. 線程的深入解析
<a name="eu89M"></a>
3.1 線程的啟動(dòng)原理
- 前面我們通過(guò)一些案例演示了線程的啟動(dòng),也就是調(diào)用
start()方法去啟動(dòng)一個(gè)線程,當(dāng)run方法中的代碼執(zhí)行完畢以后,線程的生命周期也將終止。調(diào)用start方法的語(yǔ)義是當(dāng)前線程告訴JVM,啟動(dòng)調(diào)用start方法的線程。 - 我們開(kāi)始學(xué)習(xí)線程時(shí)很大的疑惑就是 啟動(dòng)一個(gè)線程是使用
start方法,而不是直接調(diào)用run方法,這里我們首先簡(jiǎn)單看一下start方法的定義,在Thread類中
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
//線程調(diào)用的核心方法,這是一個(gè)本地方法,native
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//線程調(diào)用的 native 方法
private native void start0();
- 這里我們能看到
start方法中調(diào)用了native方法start0來(lái)啟動(dòng)線程,這個(gè)方法是在Thread類中的靜態(tài)代碼塊中注冊(cè)的 , 這里直接調(diào)用了一個(gè)native方法registerNatives
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
由于
registerNatives方法是本地方法,我們要看其實(shí)現(xiàn)源碼則必須去下載jdk源碼,關(guān)于jdk及虛擬機(jī)hotspot的源碼下載可以去openJDK官網(wǎng)下載 ,參考:我們可以本地查看源碼或者直接去 http://hg.openjdk.java.net/jdk8u/jdk8u60/jdk/file/935758609767/src/share/native/java/lang/Thread.c 查看
Thread類對(duì)應(yīng)的本地方法.c文件,

- 如上圖,我們本地下載
jdk工程,找到src->share->native->java->lang->Thread.c文件

- 上面是
Thread.c中所有代碼,我們可以看到調(diào)用了RegisterNatives同時(shí)可以看到method集合中的映射,在調(diào)用本地方法start0時(shí),實(shí)際調(diào)用了JVM_StartThread,它自身是由c/c++實(shí)現(xiàn)的,這里需要在 虛擬機(jī)源碼中去查看,我們使用的都是hostpot虛擬機(jī),這個(gè)可以去openJDK官網(wǎng)下載,上述介紹了不再多說(shuō) - 我們看到
JVM_StartThread的定義是在jvm.h源碼中,而jvm.h的實(shí)現(xiàn)則在虛擬機(jī)hotspot中,我們打開(kāi)hotspot源碼,找到src -> share -> vm -> prims ->jvm.cpp文件,在2955行,可以直接檢索JVM_StartThread, 方法代碼如下:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;
{
MutexLocker mu(Threads_lock);
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
// We could also check the stillborn flag to see if this thread was already stopped, but
// for historical reasons we let the thread detect that itself when it starts running
// <1> :獲取當(dāng)前進(jìn)程中線程的數(shù)量
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
size_t sz = size > 0 ? (size_t) size : 0;
// <2> :真正調(diào)用創(chuàng)建線程的方法
native_thread = new JavaThread(&thread_entry, sz);
if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
assert(native_thread != NULL, "Starting null thread?");
if (native_thread->osthread() == NULL) {
// No one should hold a reference to the 'native_thread'.
delete native_thread;
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
"unable to create new native thread");
}
THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
"unable to create new native thread");
}
// <3> 啟動(dòng)線程
Thread::start(native_thread);
JVM_END
JVM_ENTRY是用來(lái)定義JVM_StartThread函數(shù)的,在這個(gè)函數(shù)里面創(chuàng)建了一個(gè)真正和平臺(tái)有關(guān)的本地線程, 上述標(biāo)記 <2> 處
- 為了進(jìn)一步線程創(chuàng)建,我們?cè)谶M(jìn)入
new JavaThread(&thread_entry, sz)中查看一下具體實(shí)現(xiàn)過(guò)程,在thread.cpp文件1566行處定義了new的方法

- 對(duì)于上述代碼我們可以看到最終調(diào)用了
os::create_thread(this, thr_type, stack_sz);來(lái)實(shí)現(xiàn)線程的創(chuàng)建,對(duì)于這個(gè)方法不同平臺(tái)有不同的實(shí)現(xiàn),這里不再贅述,

- 上面都是創(chuàng)建過(guò)程,之后再調(diào)用
Thread::start(native_thread);在 JVM_StartThread 中調(diào)用,該方法的實(shí)現(xiàn)在Thread.cpp中

start方法中有一個(gè)函數(shù)調(diào)用:os::start_thread(thread);,調(diào)用平臺(tái)啟動(dòng)線程的方法,最終會(huì)調(diào)用Thread.cpp文件中的JavaThread::run()方法
<br />
<a name="CXcWi"></a>
3.2 線程的終止
<a name="FyQGx"></a>
3.2.1 通過(guò)標(biāo)記位來(lái)終止線程
- 正常我們線程內(nèi)的東西都是循環(huán)執(zhí)行的,那么我們實(shí)際需求中肯定也存在想在其他線程來(lái)停止當(dāng)前線程的需要,這是后我們可以通過(guò)標(biāo)記位來(lái)實(shí)現(xiàn),所謂的標(biāo)記為其實(shí)就是
volatile修飾的變量,著由它的可見(jiàn)性特性決定的,如下代碼就是依據(jù)volatile來(lái)實(shí)現(xiàn)標(biāo)記位停止線程
//定義標(biāo)記為 使用 volatile 修飾
private static volatile boolean mark = false;
@Test
public void markTest(){
new Thread(() -> {
//判斷標(biāo)記位來(lái)確定是否繼續(xù)進(jìn)行
while (!mark){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程執(zhí)行內(nèi)容中...");
}
}).start();
System.out.println("這是主線程走起...");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//10秒后將標(biāo)記為設(shè)置 true 對(duì)線程可見(jiàn)。用volatile 修飾
mark = true;
System.out.println("標(biāo)記位修改為:"+mark);
}
<a name="3yOAk"></a>
3.2.2 通過(guò) stop 來(lái)終止線程
- 我們通過(guò)查看
Thread類或者JDK API可以看到關(guān)于線程的停止提供了stop(),supend(),resume()等方法,但是我們可以看到這些方法都被標(biāo)記了@Deprecated也就是過(guò)時(shí)的, - 雖然這幾個(gè)方法都可以用來(lái)停止一個(gè)正在運(yùn)行的線程,但是這些方法都是不安全的,都已經(jīng)被拋棄使用,所以在我們開(kāi)發(fā)中我們要避免使用這些方法,關(guān)于這些方法為什么被拋棄以及導(dǎo)致的問(wèn)題
JDK文檔中較為詳細(xì)的描述 《Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated?》 - 在其中有這樣的描述:

-
總的來(lái)說(shuō)就是:
- 調(diào)用
stop()方法會(huì)立刻停止run()方法中剩余的全部工作,包括在catch或finally等語(yǔ)句中的內(nèi)容,并拋出ThreadDeath異常(通常情況下此異常不需要顯示的捕獲),因此可能會(huì)導(dǎo)致一些工作的得不到完成,如文件,數(shù)據(jù)庫(kù)等的關(guān)閉。 - 調(diào)用
stop()方法會(huì)立即釋放該線程所持有的所有的鎖,導(dǎo)致數(shù)據(jù)得不到同步,出現(xiàn)數(shù)據(jù)不一致的問(wèn)題。
- 調(diào)用
<a name="flBbZ"></a>
3.2.3 通過(guò) interrupt 來(lái)終止線程
- 通過(guò)上面闡述,我們知道了使用
stop方法是不推薦的,那么我們用什么來(lái)更好的停止線程,這里就引出了interrupt方法,我們通過(guò)調(diào)用interrupt來(lái)中斷線程 - 當(dāng)其他線程通過(guò)調(diào)用當(dāng)前線程的
interrupt方法,表示向當(dāng)前線程打個(gè)招呼,告訴他可以中斷線程的執(zhí)行了,至于什么時(shí)候中斷,取決于當(dāng)前線程自己 - 線程通過(guò)檢查自身是否被中斷來(lái)進(jìn)行相應(yīng),可以通過(guò)
isInterrupted()來(lái)判斷是否被中斷。
<br />我們來(lái)看下面代碼:
public static void main(String[] args) {
//創(chuàng)建 interrupt-1 線程
Thread thread = new Thread(() -> {
while (true) {
//判斷當(dāng)前線程是否中斷,
if (Thread.currentThread().isInterrupted()) {
System.out.println("線程1 接收到中斷信息,中斷線程...");
break;
}
System.out.println(Thread.currentThread().getName() + "線程正在執(zhí)行...");
}
}, "interrupt-1");
//啟動(dòng)線程 1
thread.start();
//創(chuàng)建 interrupt-2 線程
new Thread(() -> {
int i = 0;
while (i <20){
System.out.println(Thread.currentThread().getName()+"線程正在執(zhí)行...");
if (i == 8){
System.out.println("設(shè)置線程中斷....");
//通知線程1 設(shè)置中斷通知
thread.interrupt();
}
i ++;
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"interrupt-2").start();
}
打印結(jié)果如下:<br />

上述代碼中我們可以看到,我們創(chuàng)建了
interrupt-1線程,其中用interrupt來(lái)判斷當(dāng)前線程是否處于中斷狀態(tài),如果處于中斷狀態(tài)那么就自然結(jié)束線程,這里的結(jié)束的具體操作由我們開(kāi)發(fā)者來(lái)決定。再創(chuàng)建interrupt-2線程,代碼相對(duì)簡(jiǎn)單不闡述,當(dāng)執(zhí)行到某時(shí)刻時(shí)將線程interrupt-1設(shè)置為中斷狀態(tài),也就是通知interrupt-1線程。
<br />線程中斷標(biāo)記復(fù)位 :<br />
在上述
interrupt-1代碼中如果加入sleep方法,那么我們會(huì)發(fā)現(xiàn)程序報(bào)出InterruptedException錯(cuò)誤,同時(shí),線程interrupt-1也不會(huì)停止,這里就是因?yàn)橹袛鄻?biāo)記被復(fù)位了 ,下面我們來(lái)介紹一下關(guān)于中斷標(biāo)記復(fù)位相關(guān)的內(nèi)容
- 在線程類中提供了** ****
Thread.interrupted** 的靜態(tài)方法,用來(lái)對(duì)線程中斷標(biāo)識(shí)的復(fù)位,在上面的代碼中,我們可以做一個(gè)小改動(dòng),對(duì)interrupt-1線程創(chuàng)建的代碼修改如下:
//創(chuàng)建 interrupt-1 線程
Thread thread = new Thread(() -> {
while (true) {
//判斷當(dāng)前線程是否中斷,
if (Thread.currentThread().isInterrupted()) {
System.out.println("線程1 接收到中斷信息,中斷線程...中斷標(biāo)記:" + Thread.currentThread().isInterrupted());
Thread.interrupted(); // //對(duì)線程進(jìn)行復(fù)位,由 true 變成 false
System.out.println("經(jīng)過(guò) Thread.interrupted() 復(fù)位后,中斷標(biāo)記:" + Thread.currentThread().isInterrupted());
//再次判斷是否中斷,如果是則退出線程
if (Thread.currentThread().isInterrupted()) {
break;
}
}
System.out.println(Thread.currentThread().getName() + "線程正在執(zhí)行...");
}
}, "interrupt-1");
上述代碼中 我們可以看到,判斷當(dāng)前線程是否處于中斷標(biāo)記為
true, 如果有其他程序通知?jiǎng)t為true此時(shí)進(jìn)入if語(yǔ)句中,對(duì)其進(jìn)行復(fù)位操作,之后再次判斷。執(zhí)行代碼后我們發(fā)現(xiàn)interrupt-1線程不會(huì)終止,而會(huì)一直執(zhí)行
-
Thread.interrupted進(jìn)行線程中斷標(biāo)記復(fù)位是一種主動(dòng)的操作行為,其實(shí)還有一種被動(dòng)的復(fù)位場(chǎng)景,那就是上面說(shuō)的當(dāng)程序出現(xiàn)InterruptedException異常時(shí),則會(huì)將當(dāng)前線程的中斷標(biāo)記狀態(tài)復(fù)位,在拋出異常前,JVM會(huì)將中斷標(biāo)記isInterrupted設(shè)置為false
在程序中,線程中斷復(fù)位的存在實(shí)際就是當(dāng)前線程對(duì)外界中斷通知信號(hào)的一種響應(yīng),但是具體響應(yīng)的內(nèi)容有當(dāng)前線程決定,線程不會(huì)立馬停止,具體是否停止等都是由當(dāng)前線程自己來(lái)決定,也就是開(kāi)發(fā)者。
<br />
<a name="HF5LA"></a>
3.3 線程終止 interrupt 的原理
- 首先我們先來(lái)看一下在
Thread中關(guān)于interrupt的定義:
public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess(); //校驗(yàn)是否有權(quán)限來(lái)修改當(dāng)前線程
// thread may be blocked in an I/O operation
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
// <1> 調(diào)用 native 方法
interrupt0(); // set interrupt status
b.interrupt(this);
return;
}
}
}
// set interrupt status
interrupt0();
}
- 上面代碼中我們可以看到,在
interrupt方法中最終調(diào)用了Native方法interrupt0,這里相關(guān)在線程啟動(dòng)時(shí)說(shuō)過(guò),不再贅述,我們直接找到hotspot中jvm.cpp文件中JVM_Interrupt方法

-
JVM_Interrupt方法比較簡(jiǎn)單,其中我們可以看到直接調(diào)用了Thread.cpp的interrupt方法,我們進(jìn)入其中查看

- 我們可以看到這里直接調(diào)用了
os::interrupt(thread)這里是調(diào)用了平臺(tái)的方法,對(duì)于不同的平臺(tái)實(shí)現(xiàn)是不同的,我們這里如下所示,選擇Linux下的實(shí)現(xiàn)os_linux.cpp中,

<br />
在上面代碼中我們可以看到,在
1處拿到OSThread,之后判斷如果interrupt為false則在2處調(diào)用OSThread的set_interrupted方法進(jìn)行設(shè)置,我們可以進(jìn)入看一下其實(shí)現(xiàn),發(fā)現(xiàn)在osThread.hpp中定義了一個(gè)成員變量volatile jint _interrupted;而set_interrupted方法其實(shí)就是將_interrupted設(shè)置為true,之后再通過(guò)ParkEvent的unpark()方法來(lái)喚醒線程。具體的過(guò)程在上面進(jìn)行的簡(jiǎn)單的注釋介紹,