一、引子
synchronized 會阻塞線程,AQS 也會阻塞線程。那麼這兩種情況,阻塞後,線程的狀態是什麼,是 waiting 還是 blocked。雖然好像知道,但不能確定。在網上搜索後,經過指引,找到 Thread.State 這個內部枚舉類型。
/**
* A thread state. A thread can be in one of the following states:
* <ul>
* <li>{@link #NEW}<br>
* A thread that has not yet started is in this state.
* </li>
* <li>{@link #RUNNABLE}<br>
* A thread executing in the Java virtual machine is in this state.
* </li>
* <li>{@link #BLOCKED}<br>
* A thread that is blocked waiting for a monitor lock
* is in this state.
* </li>
* <li>{@link #WAITING}<br>
* A thread that is waiting indefinitely for another thread to
* perform a particular action is in this state.
* </li>
* <li>{@link #TIMED_WAITING}<br>
* A thread that is waiting for another thread to perform an action
* for up to a specified waiting time is in this state.
* </li>
* <li>{@link #TERMINATED}<br>
* A thread that has exited is in this state.
* </li>
* </ul>
*
* <p>
* A thread can be in only one state at a given point in time.
* These states are virtual machine states which do not reflect
* any operating system thread states.
*
* @since 1.5
* @see #getState
*/
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* 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,
/**
* 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,
/**
* 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 <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* 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,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
註釋已經寫的很清楚了。
重點來看 WAITING 和 BLOCKED 這兩種狀態。
二、BLOCKED
A thread that is blocked waiting for a monitor lock is in this state.
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 Object#wait() Object.wait.
這樣看來,blocked 狀態僅與 synchronized 關鍵字引起線程阻塞有關。
三、WAITING
A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
Thread state for a waiting thread. A thread is in the waiting state due to calling one of the following methods:
- Object#wait() Object.wait with no timeout
- #join() Thread.join with no timeout
- LockSupport#park() LockSupport.park
A thread in the waiting state is waiting for another thread to perform a particular action.
For example, a thread that has called Object.wait() on an object is waiting for another thread to call Object.notify() or Object.notifyAll() on that object. A thread that has called Thread.join() is waiting for a specified thread to terminate.
我們知道,AQS 內部就是依賴 LockSupport.park 阻塞線程,所以在 AQS 中被阻塞的線程處於 waiting 狀態。
四、總結
blocked 和 waiting 是 Java 線程的兩種阻塞狀態。
因爲爭用 synchronized 的 monitor 對象而發生阻塞的線程處於 blocked 狀態。
而 AQS 中的阻塞線程處於 waiting 狀態。
兩種狀態的區別:
兩種狀態對應的場景的區別,源碼中的註釋已經講的很清楚了。
但既然都是阻塞,還要分成這兩種,除了場景不同外,肯定還有底層更深層次的原因。
個人認爲更加本質的區別是,blocked 狀態指的是進行系統調用,通過操作系統掛起線程後,線程的狀態。而 waiting 狀態則不需要進行系統調用,是一種 JVM 層面的線程阻塞後的狀態。由於轉換到 blocked 狀態需要進行系統調用,所以到這個狀態的轉換操作比較重。
至於系統調用爲什麼比較重,可以參考 爲什麼系統調用比普通的函數調用更耗時?用戶態和內核態切換的代價在哪?