
一、前提
最近有點(diǎn)懶散,沒(méi)什么比較有深度的產(chǎn)出。剛好想重新研讀一下JUC線程池的源碼實(shí)現(xiàn),在此之前先深入了解一下Java中的線程實(shí)現(xiàn),包括線程的生命周期、狀態(tài)切換以及線程的上下文切換等等。編寫(xiě)本文的時(shí)候,使用的JDK版本是11。
二、Java線程的實(shí)現(xiàn)
在JDK1.2之后,Java線程模型已經(jīng)確定了基于操作系統(tǒng)原生線程模型實(shí)現(xiàn)。因此,目前或者今后的JDK版本中,操作系統(tǒng)支持怎么樣的線程模型,在很大程度上決定了Java虛擬機(jī)的線程如何映射,這一點(diǎn)在不同的平臺(tái)上沒(méi)有辦法達(dá)成一致,虛擬機(jī)規(guī)范中也未限定Java線程需要使用哪種線程模型來(lái)實(shí)現(xiàn)。線程模型只對(duì)線程的并發(fā)規(guī)模和操作成本產(chǎn)生影響,對(duì)于Java程序來(lái)說(shuō),這些差異是透明的。
對(duì)應(yīng)Oracle Sun JDK或者說(shuō)Oracle Sun JVM而言,它的Windows版本和Linux版本都是使用一對(duì)一的線程模型實(shí)現(xiàn)的(如下圖所示)。

也就是一條Java線程就映射到一條輕量級(jí)進(jìn)程(Light Weight Process)中,而一條輕量級(jí)線程又映射到一條內(nèi)核線程(Kernel-Level Thread)。我們平時(shí)所說(shuō)的線程,往往就是指輕量級(jí)進(jìn)程(或者說(shuō)我們平時(shí)新建的java.lang.Thread就是輕量級(jí)進(jìn)程實(shí)例)。前面推算這個(gè)線程映射關(guān)系,可以知道,我們?cè)趹?yīng)用程序中創(chuàng)建或者操作的java.lang.Thread實(shí)例最終會(huì)映射到系統(tǒng)的內(nèi)核線程,如果我們惡意或者實(shí)驗(yàn)性無(wú)限創(chuàng)建java.lang.Thread實(shí)例,最終會(huì)影響系統(tǒng)的正常運(yùn)行甚至導(dǎo)致系統(tǒng)崩潰(可以在Windows開(kāi)發(fā)環(huán)境中做實(shí)驗(yàn),確保內(nèi)存足夠的情況下使用死循環(huán)創(chuàng)建和運(yùn)行java.lang.Thread實(shí)例)。
線程調(diào)度方式包括兩種,協(xié)同式線程調(diào)度和搶占式線程調(diào)度。
| 線程調(diào)度方式 | 描述 | 劣勢(shì) | 優(yōu)勢(shì) |
|---|---|---|---|
| 協(xié)同式線程調(diào)度 | 線程的執(zhí)行時(shí)間由線程本身控制,執(zhí)行完畢后主動(dòng)通知操作系統(tǒng)切換到另一個(gè)線程上 | 某個(gè)線程如果不讓出CPU執(zhí)行時(shí)間可能會(huì)導(dǎo)致整個(gè)系統(tǒng)崩潰 | 實(shí)現(xiàn)簡(jiǎn)單,沒(méi)有線程同步的問(wèn)題 |
| 搶占式線程調(diào)度 | 每個(gè)線程由操作系統(tǒng)來(lái)分配執(zhí)行時(shí)間,線程的切換不由線程自身決定 | 實(shí)現(xiàn)相對(duì)復(fù)雜,操作系統(tǒng)需要控制線程同步和切換 | 不會(huì)出現(xiàn)一個(gè)線程阻塞導(dǎo)致系統(tǒng)崩潰的問(wèn)題 |
Java線程最終會(huì)映射為系統(tǒng)內(nèi)核原生線程,所以Java線程調(diào)度最終取決于系操作系統(tǒng),而目前主流的操作系統(tǒng)內(nèi)核線程調(diào)度基本都是使用搶占式線程調(diào)度。也就是可以死記硬背一下:Java線程是使用搶占式線程調(diào)度方式進(jìn)行線程調(diào)度的。
很多操作系統(tǒng)都提供線程優(yōu)先級(jí)的概念,但是由于平臺(tái)特性的問(wèn)題,Java中的線程優(yōu)先級(jí)和不同平臺(tái)中系統(tǒng)線程優(yōu)先級(jí)并不匹配,所以Java線程優(yōu)先級(jí)可以僅僅理解為“建議優(yōu)先級(jí)”,通俗來(lái)說(shuō)就是java.lang.Thread#setPriority(int newPriority)并不一定生效,有可能Java線程的優(yōu)先級(jí)會(huì)被系統(tǒng)自行改變。
三、Java線程的狀態(tài)切換
Java線程的狀態(tài)可以從java.lang.Thread的內(nèi)部枚舉類(lèi)java.lang.Thread$State得知:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
這些狀態(tài)的描述總結(jié)成圖如下:

線程狀態(tài)之間關(guān)系切換圖如下:

1. NEW狀態(tài)
API注釋?zhuān)?/p>
/**
* Thread state for a thread which has not yet started.
*
*/
NEW,
線程實(shí)例尚未啟動(dòng)時(shí)候的線程狀態(tài)。
一個(gè)剛創(chuàng)建而尚未啟動(dòng)(尚未調(diào)用Thread#start()方法)的Java線程實(shí)例的就是出于NEW狀態(tài)。
public class ThreadState {
public static void main(String[] args) throws Exception {
Thread thread = new Thread();
System.out.println(thread.getState());
}
}
// 輸出結(jié)果
NEW
2. RUNNABLE狀態(tài)
API注釋?zhuān)?/p>
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
可運(yùn)行狀態(tài)下線程的線程狀態(tài)??蛇\(yùn)行狀態(tài)下的線程在Java虛擬機(jī)中執(zhí)行,但它可能執(zhí)行等待操作系統(tǒng)的其他資源,例如處理器。
當(dāng)Java線程實(shí)例調(diào)用了Thread#start()之后,就會(huì)進(jìn)入RUNNABLE狀態(tài)。RUNNABLE狀態(tài)可以認(rèn)為包含兩個(gè)子狀態(tài):READY和RUNNING。
- READY:該狀態(tài)的線程可以被線程調(diào)度器進(jìn)行調(diào)度使之更變?yōu)镽UNNING狀態(tài)。
- RUNNING:該狀態(tài)表示線程正在運(yùn)行,線程對(duì)象的run()方法中的代碼所對(duì)應(yīng)的的指令正在被CPU執(zhí)行。
當(dāng)Java線程實(shí)例Thread#yield()方法被調(diào)用時(shí)或者由于線程調(diào)度器的調(diào)度,線程實(shí)例的狀態(tài)有可能由RUNNING轉(zhuǎn)變?yōu)镽EADY,但是從線程狀態(tài)Thread#getState()獲取到的狀態(tài)依然是RUNNABLE。例如:
public class ThreadState1 {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(()-> {
while (true){
Thread.yield();
}
}
);
thread.start();
Thread.sleep(2000);
System.out.println(thread.getState());
}
}
// 輸出結(jié)果
RUNNABLE
3. WAITING狀態(tài)
API注釋?zhuān)?/p>
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called {@code Object.wait()}
* on an object is waiting for another thread to call
* {@code Object.notify()} or {@code Object.notifyAll()} on
* that object. A thread that has called {@code Thread.join()}
* is waiting for a specified thread to terminate.
*/
WAITING,
等待中線程的狀態(tài)。一個(gè)線程進(jìn)入等待狀態(tài)是由于調(diào)用了下面方法之一:
不帶超時(shí)的Object#wait()
不帶超時(shí)的Thread#join()
LockSupport.park()
一個(gè)處于等待狀態(tài)的線程總是在等待另一個(gè)線程進(jìn)行一些特殊的處理。
例如:一個(gè)線程調(diào)用了Object#wait(),那么它在等待另一個(gè)線程調(diào)用對(duì)象上的Object#notify()或者Object#notifyAll();一個(gè)線程調(diào)用了Thread#join(),那么它在等待另一個(gè)線程終結(jié)。
WAITING是無(wú)限期的等待狀態(tài),這種狀態(tài)下的線程不會(huì)被分配CPU執(zhí)行時(shí)間。當(dāng)一個(gè)線程執(zhí)行了某些方法之后就會(huì)進(jìn)入無(wú)限期等待狀態(tài),直到被顯式喚醒,被喚醒后,線程狀態(tài)由WAITING更變?yōu)镽UNNABLE然后繼續(xù)執(zhí)行。
| RUNNABLE轉(zhuǎn)換為WAITING的方法(無(wú)限期等待) | WAITING轉(zhuǎn)換為RUNNABLE的方法(喚醒) |
|---|---|
| Object#wait() | Object#notify()或者Object#notifyAll() |
| Thread#join() | - |
| LockSupport.part() | LockSupport.unpart(thread) |
其中Thread#join()方法相對(duì)比較特殊,它會(huì)阻塞線程實(shí)例直到線程實(shí)例執(zhí)行完畢,可以觀察它的源碼如下:
public final void join() throws InterruptedException {
join(0);
}
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;
}
}
}
可見(jiàn)Thread#join()是在線程實(shí)例存活的時(shí)候總是調(diào)用Object#wait()方法,也就是必須在線程執(zhí)行完畢isAlive()為false(意味著線程生命周期已經(jīng)終結(jié))的時(shí)候才會(huì)解除阻塞。
基于WAITING狀態(tài)舉個(gè)例子:
public class ThreadState3 {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(()-> {
LockSupport.park();
while (true){
Thread.yield();
}
}
);
thread.start();
Thread.sleep(50);
System.out.println(thread.getState());
LockSupport.unpark(thread);
Thread.sleep(50);
System.out.println(thread.getState());
}
}
// 輸出結(jié)果
WAITING
RUNNABLE
4. TIMED WAITING狀態(tài)
API注釋?zhuān)?/p>
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
定義了具體等待時(shí)間的等待中線程的狀態(tài)。一個(gè)線程進(jìn)入該狀態(tài)是由于指定了具體的超時(shí)期限調(diào)用了下面方法之一:
Thread.sleep()
帶超時(shí)的Object#wait()
帶超時(shí)的Thread#join()
LockSupport.parkNanos()
LockSupport.parkUntil()
TIMED WAITING就是有限期等待狀態(tài),它和WAITING有點(diǎn)相似,這種狀態(tài)下的線程不會(huì)被分配CPU執(zhí)行時(shí)間,不過(guò)這種狀態(tài)下的線程不需要被顯式喚醒,只需要等待超時(shí)限期到達(dá)就會(huì)被VM喚醒,有點(diǎn)類(lèi)似于現(xiàn)實(shí)生活中的鬧鐘。
| RUNNABLE轉(zhuǎn)換為T(mén)IMED WAITING的方法(有限期等待) | TIMED WAITING轉(zhuǎn)換為RUNNABLE的方法(超時(shí)解除等待) |
|---|---|
| Object#wait(timeout) | - |
| Thread#sleep(timeout) | - |
| Thread#join(timeout) | - |
| LockSupport.parkNanos(timeout) | - |
| LockSupport.parkUntil(timeout) | - |
舉個(gè)例子:
public class ThreadState4 {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(()-> {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
//ignore
}
}
);
thread.start();
Thread.sleep(50);
System.out.println(thread.getState());
Thread.sleep(1000);
System.out.println(thread.getState());
}
}
// 輸出結(jié)果
TIMED_WAITING
TERMINATED
5. BLOCKED狀態(tài)
API注釋?zhuān)?/p>
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
此狀態(tài)表示一個(gè)線程正在阻塞等待獲取一個(gè)監(jiān)視器鎖。如果線程處于阻塞狀態(tài),說(shuō)明線程等待進(jìn)入同步代碼塊或者同步方法的監(jiān)視器鎖或者在調(diào)用了Object#wait()之后重入同步代碼塊或者同步方法。
BLOCKED狀態(tài)也就是阻塞狀態(tài),該狀態(tài)下的線程不會(huì)被分配CPU執(zhí)行時(shí)間。線程的狀態(tài)為BLOCKED的時(shí)候有兩種可能的情況:
A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method
- 線程正在等待一個(gè)監(jiān)視器鎖,只有獲取監(jiān)視器鎖之后才能進(jìn)入synchronized代碼塊或者synchronized方法,在此等待獲取鎖的過(guò)程線程都處于阻塞狀態(tài)。
reenter a synchronized block/method after calling Object#wait()
- 線程X步入synchronized代碼塊或者synchronized方法后(此時(shí)已經(jīng)釋放監(jiān)視器鎖)調(diào)用Object#wait()方法之后進(jìn)行阻塞,當(dāng)接收其他線程T調(diào)用該鎖對(duì)象Object#notify()/notifyAll(),但是線程T尚未退出它所在的synchronized代碼塊或者synchronized方法,那么線程X依然處于阻塞狀態(tài)(注意API注釋中的reenter,理解它場(chǎng)景2就豁然開(kāi)朗)。
針對(duì)上面的場(chǎng)景1舉個(gè)簡(jiǎn)單的例子:
public class ThreadState6 {
private static final Object MONITOR = new Object();
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(()-> {
synchronized (MONITOR){
try {
Thread.sleep(Integer.MAX_VALUE);
}
catch (InterruptedException e) {
//ignore
}
}
}
);
Thread thread2 = new Thread(()-> {
synchronized (MONITOR){
System.out.println("thread2 got monitor lock...");
}
}
);
thread1.start();
Thread.sleep(50);
thread2.start();
Thread.sleep(50);
System.out.println(thread2.getState());
}
}
// 輸出結(jié)果
BLOCKED
針對(duì)上面的場(chǎng)景2舉個(gè)簡(jiǎn)單的例子:
public class ThreadState7 {
private static final Object MONITOR = new Object();
private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws Exception {
System.out.println(String.format("[%s]-begin...", F.format(LocalDateTime.now())));
Thread thread1 = new Thread(() -> {
synchronized (MONITOR) {
System.out.println(String.format("[%s]-thread1 got monitor lock...", F.format(LocalDateTime.now())));
try {
Thread.sleep(1000);
MONITOR.wait();
}
catch (InterruptedException e) {
//ignore
}
System.out.println(String.format("[%s]-thread1 exit waiting...", F.format(LocalDateTime.now())));
}
}
);
Thread thread2 = new Thread(() -> {
synchronized (MONITOR) {
System.out.println(String.format("[%s]-thread2 got monitor lock...", F.format(LocalDateTime.now())));
try {
MONITOR.notify();
Thread.sleep(2000);
}
catch (InterruptedException e) {
//ignore
}
System.out.println(String.format("[%s]-thread2 releases monitor lock...", F.format(LocalDateTime.now())));
}
}
);
thread1.start();
thread2.start();
// 這里故意讓主線程sleep 1500毫秒從而讓thread2調(diào)用了Object#notify()并且尚未退出同步代碼塊,確保thread1調(diào)用了Object#wait()
Thread.sleep(1500);
System.out.println(thread1.getState());
System.out.println(String.format("[%s]-end...", F.format(LocalDateTime.now())));
}
}
// 某個(gè)時(shí)刻的輸出如下:
[2019-06-20 00:30:22]-begin...
[2019-06-20 00:30:22]-thread1 got monitor lock...
[2019-06-20 00:30:23]-thread2 got monitor lock...
BLOCKED
[2019-06-20 00:30:23]-end...
[2019-06-20 00:30:25]-thread2 releases monitor lock...
[2019-06-20 00:30:25]-thread1 exit waiting...
場(chǎng)景2中:
- 線程2調(diào)用Object#notify()后睡眠2000毫秒再退出同步代碼塊,釋放監(jiān)視器鎖。
- 線程1只睡眠了1000毫秒就調(diào)用了Object#wait(),此時(shí)它已經(jīng)釋放了監(jiān)視器鎖,所以線程2成功進(jìn)入同步塊,線程1處于API注釋中所述的reenter a synchronized block/method的狀態(tài)。
- 主線程睡眠1500毫秒剛好可以命中線程1處于reenter狀態(tài)并且打印其線程狀態(tài),剛好就是BLOCKED狀態(tài)。
這三點(diǎn)看起來(lái)有點(diǎn)繞,多看幾次多思考一下應(yīng)該就能理解。
6. TERMINATED狀態(tài)
API注釋?zhuān)?/p>
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
終結(jié)的線程對(duì)應(yīng)的線程狀態(tài),此時(shí)線程已經(jīng)執(zhí)行完畢。
TERMINATED狀態(tài)表示線程已經(jīng)終結(jié)。一個(gè)線程實(shí)例只能被啟動(dòng)一次,準(zhǔn)確來(lái)說(shuō),只會(huì)調(diào)用一次Thread#run()方法,Thread#run()方法執(zhí)行結(jié)束之后,線程狀態(tài)就會(huì)更變?yōu)門(mén)ERMINATED,意味著線程的生命周期已經(jīng)結(jié)束。
舉個(gè)簡(jiǎn)單的例子:
public class ThreadState8 {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
}
);
thread.start();
Thread.sleep(50);
System.out.println(thread.getState());
}
}
// 輸出結(jié)果
TERMINATED
四、上下文切換
多線程環(huán)境中,當(dāng)一個(gè)線程的狀態(tài)由RUNNABLE轉(zhuǎn)換為非RUNNABLE(BLOCKED、WAITING或者TIMED_WAITING)時(shí),相應(yīng)線程的上下文信息(也就是常說(shuō)的Context,包括CPU的寄存器和程序計(jì)數(shù)器在某一時(shí)間點(diǎn)的內(nèi)容等等)需要被保存,以便線程稍后恢復(fù)為RUNNABLE狀態(tài)時(shí)能夠在之前的執(zhí)行進(jìn)度的基礎(chǔ)上繼續(xù)執(zhí)行。而一個(gè)線程的狀態(tài)由非RUNNABLE狀態(tài)進(jìn)入RUNNABLE狀態(tài)時(shí)可能涉及恢復(fù)之前保存的線程上下文信息并且在此基礎(chǔ)上繼續(xù)執(zhí)行。這里的對(duì)線程的上下文信息進(jìn)行保存和恢復(fù)的過(guò)程就稱為上下文切換(Context Switch)。
線程的上下文切換會(huì)帶來(lái)額外的性能開(kāi)銷(xiāo),這包括保存和恢復(fù)線程上下文信息的開(kāi)銷(xiāo)、對(duì)線程進(jìn)行調(diào)度的CPU時(shí)間開(kāi)銷(xiāo)以及CPU緩存內(nèi)容失效的開(kāi)銷(xiāo)(線程所執(zhí)行的代碼從CPU緩存中訪問(wèn)其所需要的變量值要比從主內(nèi)存(RAM)中訪問(wèn)響應(yīng)的變量值要快得多,但是線程上下文切換會(huì)導(dǎo)致相關(guān)線程所訪問(wèn)的CPU緩存內(nèi)容失效,一般是CPU的L1 Cache和L2 Cache,使得相關(guān)線程稍后被重新調(diào)度到運(yùn)行時(shí)其不得不再次訪問(wèn)主內(nèi)存中的變量以重新創(chuàng)建CPU緩存內(nèi)容)。
在Linux系統(tǒng)中,可以通過(guò)vmstat命令來(lái)查看全局的上下文切換的次數(shù),例如:
$ vmstat 1
對(duì)于Java程序的運(yùn)行,在Linux系統(tǒng)中也可以通過(guò)perf命令進(jìn)行監(jiān)視,例如:
$ perf stat -e cpu-clock,task-clock,cs,cache-reference,cache-misses java YourJavaClass
參考資料中提到Windows系統(tǒng)下可以通過(guò)自帶的工具perfmon(其實(shí)也就是任務(wù)管理器)來(lái)監(jiān)視線程的上下文切換,實(shí)際上筆者并沒(méi)有從任務(wù)管理器發(fā)現(xiàn)有任何辦法查看上下文切換,通過(guò)搜索之后發(fā)現(xiàn)了一個(gè)工具:Process Explorer。運(yùn)行Process Explorer同時(shí)運(yùn)行一個(gè)Java程序并且查看其狀態(tài):

因?yàn)榇蛄藬帱c(diǎn),可以看到運(yùn)行中的程序的上下文切換一共7000多次,當(dāng)前一秒的上下文切換增量為26(因?yàn)楣P者設(shè)置了Process Explorer每秒刷新一次數(shù)據(jù))。
五、監(jiān)控線程狀態(tài)
如果項(xiàng)目在生產(chǎn)環(huán)境中運(yùn)行,不可能頻繁調(diào)用Thread#getState()方法去監(jiān)測(cè)線程的狀態(tài)變化。JDK本身提供了一些監(jiān)控線程狀態(tài)的工具,還有一些開(kāi)源的輕量級(jí)工具如阿里的Arthas,這里簡(jiǎn)單介紹一下。
1. 使用jvisualvm
jvisualvm是JDK自帶的堆、線程等待JVM指標(biāo)監(jiān)控工具,適合使用于開(kāi)發(fā)和測(cè)試環(huán)境。它位于JAVA_HOME/bin目錄之下。

其中線程Dump的按鈕類(lèi)似于下面要提到的jstack命令,用于導(dǎo)出所有線程的棧信息。
2. 使用jstack
jstack是JDK自帶的命令行工具,功能是用于獲取指定PID的Java進(jìn)程的線程棧信息。例如本地運(yùn)行的一個(gè)IDEA實(shí)例的PID是11376,那么只需要輸入:
jstack 11376

另外,如果想要定位具體Java進(jìn)程的PID,可以使用jps命令。
3. 使用JMC
JMC也就是Java Mission Control,它也是JDK自帶的工具,提供的功能要比jvisualvm強(qiáng)大,包括MBean的處理、線程棧以及線程狀態(tài)查看、飛行記錄器等等。
六、小結(jié)
理解Java線程狀態(tài)的切換和一些監(jiān)控手段,更有利于日常開(kāi)發(fā)多線程程序,對(duì)于生產(chǎn)環(huán)境出現(xiàn)問(wèn)題,通過(guò)監(jiān)控線程的棧信息能夠快速定位到問(wèn)題的根本原因(通常來(lái)說(shuō),目前比較主流的MVC應(yīng)用都是通過(guò)一個(gè)線程處理一個(gè)單獨(dú)的請(qǐng)求,當(dāng)請(qǐng)求出現(xiàn)阻塞的時(shí)候,導(dǎo)出對(duì)應(yīng)處理請(qǐng)求的線程基本可以定位到阻塞的精準(zhǔn)位置,如果使用消息隊(duì)列例如RabbitMQ,消費(fèi)者線程出現(xiàn)阻塞也可以利用相似的思路解決)。
寫(xiě)在最后
- 第一:看完點(diǎn)贊,感謝您對(duì)作者的認(rèn)可;
- ...
- 第二:隨手轉(zhuǎn)發(fā),分享知識(shí),讓更多人學(xué)習(xí)到;
- ...
- 第三:記得點(diǎn)關(guān)注,每天更新的?。。?/strong>
- ...