程序猿學社的GitHub,歡迎Star
https://github.com/ITfqyd/cxyxs
本文已記錄到github,形成對應專題。
文章目錄
前言
聽過前面幾篇文章的閱讀,我們對多線程已經有了大致的瞭解。世界上的萬物都是有規律的,人有生命週期,幼兒期->少年->青年,當然線程也不例外。跟着社長一起來看看線程的生老病死是怎麼一回事。
1.通過查看源碼瞭解線程的生命週期
首先先敲出Thread,查看該類的源碼,通過Alt+7(idea)查看類的所有方法。
通過該類我們可以發現有一個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;
}
/**
* Returns the state of this thread.
* This method is designed for use in monitoring of the system state,
* not for synchronization control.
*
* @return this thread's state.
* @since 1.5
*/
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
getState方法獲取當前線程的狀態。
通過這個枚舉類,我們可以看出線程的生命週期有6種
- NEW(新建)
- RUNNABLE(就緒)
- BLOCKED(阻塞)
- WAITING(等待)
- TIMED_WAITING(限時等待)
- TERMINATED(死亡或者完成)
2.通過代碼更好的理解線程6種週期狀態
NEW(新建)
單單創建一個線程,不運行,就是線程的新建狀態。
public class New {
public static void main(String[] args) {
Thread t = new Thread();
System.out.println(t.getState());
}
}
RUNNABLE(就緒)
一個線程調用start方法後,表示他已經在jvm中運行了,還沒有被cpu資源調度。就稱之就緒狀態。
package com.cxyxs.thread.five;
/**
* Description:轉發請註明來源 程序猿學社 - https://ithub.blog.csdn.net/
* Author: 程序猿學社
* Date: 2020/2/22 11:08
* Modified By:
*/
public class RUNNABLE {
public static void main(String[] args) {
Thread t = new Thread();
t.start();
System.out.println(t.getState());
}
}
TERMINATED(死亡或者完成)
package com.cxyxs.thread.five;
/**
* Description:轉發請註明來源 程序猿學社 - https://ithub.blog.csdn.net/
* Author: 程序猿學社
* Date: 2020/2/22 11:24
* Modified By:
*/
public class TERMINATED {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread();
t.start();
Thread.sleep(5000);
System.out.println(t.getState());
}
}
結合上RUNNABLE就緒狀態的代碼和結果,各位社友,覺得爲什麼我增加一句Thread.sleep後,代碼的狀態就發生改變咯?
首先回答這個問題之前,我們需要了解時間片的概念
我們調用線程的start方法以後,不是馬上就執行,而是需要通過cpu的資源調度,假設,只有一個cpu,一個通道,同時存在多個線程,因爲同一時間,只可能有一個線程搶到資源,cpu會給這個線程分配一個時間片,如果在固定時間內,還沒有處理完,爲了保證公平公正的原則,cpu會切下一個線程,在固定時間內處理完,也會切下一個線程。這樣就不會造成資源的浪費。
- 調用start方法以後,我延遲5秒鐘,就是保證線程已經跑完了,所以最終的結果爲TERMINATED
BLOCKED(阻塞)
當前線程在等待一個monitor lock的過程。例如執行同步代碼塊或者同步方法,都會導致線程處於堵塞狀態。
package com.cxyxs.thread.five;
/**
* Description:轉發請註明來源 程序猿學社 - https://ithub.blog.csdn.net/
* Author: 程序猿學社
* Date: 2020/2/22 14:13
* Modified By:
*/
public class BLOCKED {
public static synchronized void test() throws Exception{
//模擬業務
Thread.sleep(3000);
}
public static void main(String[] args) throws Exception{
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
test();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread thread1= new Thread(runnable);
thread1.start();
Thread.sleep(100);
System.out.println(thread1.getState());
}
}
這段代碼就兩處關鍵的步驟
- 第一步,啓動一個同步方法,就是相當於上鎖,多個線程訪問時,需要拿到鎖的資格後,才能進行訪問該訪問。
- 第二步 休眠100毫秒,這個代碼很關鍵,就是爲了保證線程已經開始運行了。
案例:
隔壁小王家就一個茅坑,小王進去後,就直接把茅坑的們反鎖勒,其他的人,需要等小王用完後,才能進入。其他的人就堵塞在這裏。
下面幾種方法會導致阻塞
- 同步方法或者同步代碼塊
- Object.wait
WAITING(等待)
一個線程在等待另一個線程被喚醒,這個狀態就是等待。
package com.cxyxs.thread.five;
/**
* Description:轉發請註明來源 程序猿學社 - https://ithub.blog.csdn.net/
* Author: 程序猿學社
* Date: 2020/2/22 14:46
* Modified By:
*/
public class WAITING {
public static void main(String[] args) throws Exception{
Object locker = new Object();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
synchronized (locker){
System.out.println("調用wait");
locker.wait();
System.out.println("調用wait後");
}
System.out.println("調用wait業務");
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread t = new Thread(runnable);
t.start();
Thread.sleep(100);
runnable = new Runnable() {
@Override
public void run() {
try {
synchronized (locker){
System.out.println("調用notify");
locker.notify();
System.out.println("調用notify後");
}
System.out.println("調用notif業務");
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread t1 = new Thread(runnable);
t1.start();
System.out.println(t.getState()+","+t1.getState());
}
}
通過線程t1喚醒線程t,休眠100毫秒,是爲了保證線程已經運行起來,線程t調用wait,表示線程進入線程等待隊列,當前這個線程t會堵塞在這裏,不會進行向下運行,需要另外一個線程把他喚醒,同步鎖是locker ,獲取鎖以後,調用notify,喚醒線程t.
可能導致WAITING的幾條
- Object.wait
- Thread.join
- LockSupport.park
通過Object.notify進行喚醒
TIMED_WAITING(限時等待)
一個線程在一個特定的時間內等待另一個線程完成會有這麼一個狀態,自動喚醒,稱爲限時等待。
package com.cxyxs.thread.five;
/**
* Description:轉發請註明來源 程序猿學社 - https://ithub.blog.csdn.net/
* Author: 程序猿學社
* Date: 2020/2/22 13:42
* Modified By:
*/
public class TIMEDWAITING{
public static void main(String[] args) throws Exception{
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//1
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
//2
Thread.sleep(100);
System.out.println(thread.getState());
}
}
第一步,延遲5秒,就是爲了讓出資源
第二步,延遲100豪秒,就是爲了保證線程已經在執行狀態。並保證這個線程搶到時間片。
案例:
隔壁老王打算去上班,在等待公交車,等了半天,發現沒有座位,想了想,還是沒有上車,最後,等了下一班車,再去的公司。
實際上,就是我已經在cpu資源裏面拿到時間片資格了,但是,由於這個同學,十分的懶,想要舒服一點,所以他就把這個上車的資格讓給了其他人。當然,最後,小王也順利的到達勒公司。
下面幾種方法會導致限制等待
- Thread.sleep
- Object.wait
- Thread.join
- LockSupport.parkNanos
- LockSupport.parkUntil
6種狀態總結
面試遇到問生命週期,可以從這兩張圖開始說起。先從下面這個圖開始,再到第一個圖。先從大的方向開始說起,再到底層源碼是怎麼樣實現的。
後記
下篇文章,會結合實現的demo,瞭解Thread中一些常用方法。