JDK源碼解析之 java.lang.Thread

位于java.lang包下的Thread類是非常重要的線程類,它實現了Runnable接口,今天我們來學習一下Thread類,在學習Thread類之前,先介紹與線程相關知識:線程的幾種狀態、上下文切換,然后接著介紹Thread類中的方法的具體使用

一、線程的狀態

線程從創建到最終的消亡,要經歷若干個狀態。一般來說,線程包括以下這幾個狀態:

創建(new)、就緒(runnable)、運行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)

當需要新起一個線程來執行某個子任務時,就創建了一個線程。但是線程創建之后,不會立即進入就緒狀態,因為線程的運行需要一些條件(比如內存資源,譬如程序計數器、Java棧、本地方法棧都是線程私有的,所以需要為線程分配一定的內存空間),只有線程運行需要的所有條件滿足了,才進入就緒狀態。

當線程進入就緒狀態后,不代表立刻就能獲取CPU執行時間,也許此時CPU正在執行其他的事情,因此它要等待。當得到CPU執行時間之后,線程便真正進入運行狀態。

線程在運行狀態過程中,可能有多個原因導致當前線程不繼續運行下去,比如用戶主動讓線程睡眠(睡眠一定的時間之后再重新執行)、用戶主動讓線程等待,或者被同步塊給阻塞,此時就對應著多個狀態:time waiting(睡眠或等待一定的事件)、waiting(等待被喚醒)、blocked(阻塞)。

當由于突然中斷或者子任務執行完畢,線程就會被消亡。

下面這副圖描述了線程從創建到消亡之間的狀態:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-T173LPGR-1598337004253)(/Users/marron27/Desktop/061045374695226.jpg)]

在有些教程上將blocked、waiting、time waiting統稱為阻塞狀態,這個也是可以的,只不過這里我想將線程的狀態和Java中的方法調用聯系起來,所以將waiting和time waiting兩個狀態分離出來。

二、上下文切換

對于單核CPU來說(對于多核CPU,此處就理解為一個核),CPU在一個時刻只能運行一個線程,當在運行一個線程的過程中轉去運行另外一個線程,這個叫做線程上下文切換(對于進程也是類似)。

由于可能當前線程的任務并沒有執行完畢,所以在切換時需要保存線程的運行狀態,以便下次重新切換回來時能夠繼續切換之前的狀態運行。舉個簡單的例子:比如一個線程A正在讀取一個文件的內容,正讀到文件的一半,此時需要暫停線程A,轉去執行線程B,當再次切換回來執行線程A的時候,我們不希望線程A又從文件的開頭來讀取。

因此需要記錄線程A的運行狀態,那么會記錄哪些數據呢?因為下次恢復時需要知道在這之前當前線程已經執行到哪條指令了,所以需要記錄程序計數器的值,另外比如說線程正在進行某個計算的時候被掛起了,那么下次繼續執行的時候需要知道之前掛起時變量的值時多少,因此需要記錄CPU寄存器的狀態。所以一般來說,線程上下文切換過程中會記錄程序計數器、CPU寄存器狀態等數據。

說簡單點的:對于線程的上下文切換實際上就是 存儲和恢復CPU狀態的過程,它使得線程執行能夠從中斷點恢復執行。

雖然多線程可以使得任務執行的效率得到提升,但是由于在線程切換時同樣會帶來一定的開銷代價,并且多個線程會導致系統資源占用的增加,所以在進行多線程編程時要注意這些因素。

三、類定義

class Thread implements Runnable {}

Thread實現了Runnable接口,Runnable接口是線程輔助類,僅定義了一個方法run()方法,用于實現多線程

四、成員變量

//線程的名字 
private volatile String name;
//線程的優先級
private int            priority;private Thread         threadQ;
private long           eetop;/* Whether or not to single_step this thread. */
private boolean     single_step;//是否守護進程
private boolean     daemon = false;/* JVM state */
private boolean     stillborn = false;//將要執行的任務
private Runnable target;//線程組表示一個線程的集合。此外,線程組也可以包含其他線程組。線程組構成一棵樹,在樹中,除了初始線程組外,每個線程組都有一個父線程組。
private ThreadGroup group;/* The context ClassLoader for this thread */
private ClassLoader contextClassLoader;/* The inherited AccessControlContext of this thread */
private AccessControlContext inheritedAccessControlContext;//第幾個線程,在init初始化線程的時候用來賦給thread.name
private static int threadInitNumber;ThreadLocal.ThreadLocalMap threadLocals = null;/** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;/** The requested stack size for this thread, or 0 if the creator did* not specify a stack size.  It is up to the VM to do whatever it* likes with this number; some VMs will ignore it.*/private long stackSize;/** JVM-private state that persists after native thread termination.*/private long nativeParkEventPointer;// Thread ID
private long tid;// 用來生成Thread ID使用
private static long threadSeqNumber;//線程從創建到最終的消亡,要經歷若干個狀態。 
// 一般來說,線程包括以下這幾個狀態:創建(new)、就緒(runnable)、運行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)
private volatile int threadStatus = 0;

五、常用方法

關系到線程運行狀態的幾個方法:

1.start方法

start()用來啟動一個線程,當調用start方法后,系統才會開啟一個新的線程來執行用戶定義的子任務,在這個過程中,會為相應的線程分配需要的資源。

public synchronized void start() {if (threadStatus != 0)throw new IllegalThreadStateException();group.add(this);boolean started = false;try {start0();started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {/* do nothing. If start0 threw a Throwable thenit will be passed up the call stack */}}
}
2.run方法

run()方法是不需要用戶來調用的,當通過start方法啟動一個線程之后,當線程獲得了CPU執行時間,便進入run方法體去執行具體的任務。注意,繼承Thread類必須重寫run方法,在run方法中定義具體要執行的任務。

@Override
public void run() {if (target != null) {target.run();}
}
3.sleep方法

sleep相當于讓線程睡眠,交出CPU,讓CPU去執行其他的任務。

如果需要讓當前正在執行的線程暫停一段時間,并進入阻塞狀態,則可以通過調用Thread類的靜態sleep()方法來實現。

當當前線程調用sleep()方法進入阻塞狀態后,在其睡眠時間內,該線程不會獲得執行機會,即使系統中沒有其他可執行線程,處于sleep()中的線程也不會執行,因此sleep()方法常用來暫停程序的執行

但是有一點要非常注意,sleep方法不會釋放鎖,也就是說如果當前線程持有對某個對象的鎖,則即使調用sleep方法,其他線程也無法訪問這個對象。

sleep方法有兩個重載版本:

public static native void sleep(long millis) throws InterruptedException; //參數為毫秒
public static void sleep(long millis, int nanos)//第一參數為毫秒,第二個參數為納
throws InterruptedException {if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos >= 500000 || (nanos != 0 && millis == 0)) {millis++;}sleep(millis);
}

4.yield方法

為本地方法,也就是說 yield() 是由 C 或 C++ 實現的,yield()方法和sleep()方法有點相似,它也是Thread類提供的一個靜態方法,它也可以讓當前正在執行的線程暫停,但它不會阻塞該線程,它只是將該線程轉入到就緒狀態。即讓當前線程暫停一下,讓系統的線程調度器重新調度一次,完全可能的情況是:當某個線程調用了yield()方法暫停之后,線程調度器又將其調度出來重新執行。

調用yield方法會讓當前線程交出CPU權限,讓CPU去執行其他的線程。它跟sleep方法類似,同樣不會釋放鎖。但是yield不能控制具體的交出CPU的時間,另外,當某個線程調用了yield()方法之后,只有優先級與當前線程相同或者比當前線程更高的處于就緒狀態的線程才會獲得執行機會。

注意,調用yield方法并不會讓線程進入阻塞狀態,而是讓線程重回就緒狀態,它只需要等待重新獲取CPU執行時間,這一點是和sleep方法不一樣的。

public static native void yield();

5.join方法

join方法有三個重載版本:

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;}}
}

參數為毫秒

public final synchronized void join(long millis, int nanos)
throws InterruptedException {if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos >= 500000 || (nanos != 0 && millis == 0)) {millis++;}join(millis);
}

第一參數為毫秒,第二個參數為納秒

public final void join() throws InterruptedException {join(0);
}

假如在main線程中,調用thread.join方法,則main方法會等待thread線程執行完畢或者等待一定的時間。如果調用的是無參join方法,則等待thread執行完畢,如果調用的是指定了時間參數的join方法,則等待一定的事件。

6.interrupt方法

interrupt,顧名思義,即中斷的意思。單獨調用interrupt方法可以使得處于阻塞狀態的線程拋出一個異常,也就說,它可以用來中斷一個正處于阻塞狀態的線程;另外,通過interrupt方法和isInterrupted()方法來停止正在運行的線程。

public void interrupt() {if (this != Thread.currentThread())checkAccess();synchronized (blockerLock) {Interruptible b = blocker;if (b != null) {interrupt0();           // Just to set the interrupt flagb.interrupt(this);return;}}interrupt0();
}

7.interrupted方法

interrupted()函數是Thread靜態方法,用來檢測當前線程的interrupt狀態,檢測完成后,狀態清空。通過下面的interrupted源碼我們能夠知道,此方法首先調用isInterrupted方法,而isInterrupted方法是一個重載的native方法private native boolean isInterrupted(boolean ClearInterrupted) 通過方法的注釋能夠知道,用來測試線程是否已經中斷,參數用來決定是否重置中斷標志。

public static boolean interrupted() {return currentThread().isInterrupted(true);
}public boolean isInterrupted() {return isInterrupted(false);
}private native boolean isInterrupted(boolean ClearInterrupted);

關系到線程屬性的幾個方法:

8.getId

用來得到線程ID

9.getName和setName

用來得到或者設置線程名稱。

10.getPriority和setPriority

用來獲取和設置線程優先級。

11.setDaemon和isDaemon

用來設置線程是否成為守護線程和判斷線程是否是守護線程。

守護線程和用戶線程的區別在于:守護線程依賴于創建它的線程,而用戶線程則不依賴。舉個簡單的例子:如果在main線程中創建了一個守護線程,當main方法運行完畢之后,守護線程也會隨著消亡。而用戶線程則不會,用戶線程會一直運行直到其運行完畢。在JVM中,像垃圾收集器線程就是守護線程。

Thread類有一個比較常用的靜態方法currentThread()用來獲取當前線程。

六、總結

在上面已經說到了Thread類中的大部分方法,那么Thread類中的方法調用到底會引起線程狀態發生怎樣的變化呢?下面一幅圖就是在上面的圖上進行改進而來的:

img

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/535757.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/535757.shtml
英文地址,請注明出處:http://en.pswp.cn/news/535757.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

TASKLIST

TASKLIST [/S system [/U username [/P [password]]]] [/M [module] | /SVC | /V] [/FI filter] [/FO format] [/NH]參數列表:/S system 指定連接到的遠程系統。/U [domain/]user 指定使用哪個用戶執行這個命令。/P [password] 為指定的用戶指定密碼。/SVC 顯示每個進程中的服務…

JDK源碼解析之 java.lang.ThreadLocal

此類提供線程局部變量。這些變量與普通變量不同&#xff0c;每個訪問一個線程&#xff08;通過其get或set方法&#xff09;的線程 都有其自己的&#xff0c;獨立初始化的變量副本。 ThreadLocal實例通常是希望將狀態與線程關聯的類中的私有靜態字段&#xff08;例如&#xff0c…

華爾街頂級大師胡立陽名言

1.不要聽“親朋好友”的話&#xff0c;他們只會讓你成為“平凡人”。 2.不要只會“用功讀書”&#xff0c;重要的是“要讀對書”。  3&#xff0e;不要只是“努力工作”&#xff0c;重要的是“做對工作”。   4.不要指示結交“志趣相投”的朋友&#xff0c;否則你永遠只看到…

JDK源碼解析之 Java.lang.Enum

Enum是一個特殊的類. 我們不能以class Xxx extends Enum的方式手動繼承, 必須寫成enum Xxx的形式; 然而這段枚舉類的定義在編譯之后又變回了class Xxx extends Enum. 一、類定義 public abstract class Enum<E extends Enum<E>>implements Comparable<E>, …

Linux下的一些簡單網絡配置命令介紹

1、 ifconfig可以使用ifconfig命令來配置并查看網絡接口的配置情況。例如&#xff1a;&#xff08;1&#xff09; 配置eth0的IP地址&#xff0c; 同時激活該設備。#ifconfig eth0 192.168.1.10 netmask 255.255.255.0 up&#xff08;2&#xff09; 配置eth0別名設備eth0:1的IP地…

JDK源碼解析之 java.lang.Throwable

在 Java 中&#xff0c;所有的異常都有一個共同的祖先 Throwable&#xff08;可拋出&#xff09;。Throwable 指定代碼中可用異常傳播機制通過 Java 應用程序傳輸的任何問題的共性。 一、類定義 public class Throwable implements Serializable {}Serializable&#xff1a;可…

JDK源碼解析之 java.lang.Error

java.lang.Error 錯誤。是所有錯誤的基類&#xff0c;用于標識嚴重的程序運行問題。這些問題通常描述一些不應被應用程序捕獲的反常情況。 一、源碼部分 //繼承了java.lang.Throwable public class Error extends Throwable {//適用于java序列化機制,過判斷類的serialVersionU…

linux命令之有關網絡的操作命令

1&#xff0e;hostname 命令&#xff08;1&#xff09;一般格式&#xff1a;hostname [選項] [主機名]&#xff08;2&#xff09;說明&#xff1a;顯示或設置系統的主機名&#xff1b;如果無任何選項和主機名&#xff0c;則用于顯示系統的主機名。&#xff08;3&#xff09…

JDK源碼解析之 java.lang.Exception

異常。是所有異常的基類&#xff0c;用于標識一般的程序運行問題。這些問題通常描述一些會被應用程序捕獲的反常情況。 一、源碼部分 //繼承了java.lang.Throwable public class Exception extends Throwable {//適用于java序列化機制,過判斷類的serialVersionUID來驗證的版本…

linux命令之有關關機和查看系統信息的命令

shutdown 正常關機 reboot 重啟計算機 ps 查看目前程序執行的情況top 查看目前程序執行的情景和內存使用情況kill 終止一個進程date 更改或查看目前時間 一&#xff0e;查看系統的進程 要管理進程&#xff0c;首先要知…

HDFS-文件讀寫過程

一、文件讀取 Client向NameNode發起RPC請求&#xff0c;來確定請求文件block所在的位置&#xff1b;NameNode會視情況返回文件的部分或者全部block列表&#xff0c;對于每個block&#xff0c;NameNode 都會返回含有該 block 副本的 DataNode 地址&#xff1b; 這些返回的 DN 地…

linux命令復習之有關磁盤空間的命令

1&#xff0e;mount 命令&#xff08;1&#xff09;一般格式&#xff1a;mount 文件系統類型 [選項] 掛接設備&#xff08;2&#xff09;說明&#xff1a;將某個文件系統掛載到某個目錄上。當這個命令執行成功后&#xff0c;直到使用 umount 將這個文件系統移除為止。&…

HDFS-常用API操作

一、Maven <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>RELEASE</version> </dependency> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>…

linux命令之-管理文件和目錄的命令

一. 創建和刪除目錄的命令 1&#xff0e;mkdir 命令 &#xff08;1&#xff09;一般格式&#xff1a;mkdir [選項] 目錄名 &#xff08;2&#xff09;說明&#xff1a;該命令創建由目錄名命名的目錄。 &#xff08;3&#xff09;舉例1&#xff1a; 在目錄 /usr/fedora 下建…

Hive-簡介入門

Hive簡介 Hive最初是Facebook為了滿足對海量社交網絡數據的管理和機器學習的需求而產生和發展的。互聯網現在進入了大數據時代&#xff0c;大數據是現在互聯網的趨勢&#xff0c;而hadoop就是大數據時代里的核心技術&#xff0c;但是hadoop的mapreduce操作專業性太強&#xff0…

Hive-原理解析

一、Hive 架構 下面是Hive的架構圖。 Hive的體系結構可以分為以下幾部分 1、用戶接口&#xff1a;CLI&#xff08;hive shell&#xff09;&#xff1b;JDBC&#xff08;java訪問Hive&#xff09;&#xff1b;WEBUI&#xff08;瀏覽器訪問Hive&#xff09; 2、元數據&#x…

linux命令之history命令

在Linux系統上輸入命令并按下Enter后&#xff0c;這個命令就會存放在命令記錄表 ( ~/.bash_history )中&#xff0c;預定的記錄為1000條&#xff0c;這些都定義在環境變量中。列出所有的歷史記錄&#xff1a;#history 只列出最近10條記錄&#xff1a;#history 10 (注,history和…

Hive-配置安裝

一、HDFS安裝 1、解壓到指定位置tar -zxvf apache-hive-3.1.2-bin.tar.gz -C /usr/local/apps/ 2、改名mv apache-hive-3.1.2-bin/ hive-3.1.2 3、在conf目錄下添加Hadoop安裝路徑mv hive-env.sh.template hive-env.sh # 配置HADOOP_HOME路徑 export HADOOP_HOME/opt/module/…

linux文件系統概念目錄結構

文件系統概念一. 文件與目錄的定義1. 文件系統&#xff1a;它是磁盤上有特定格式的一片區域&#xff0c;操作系統通過文件系統可以方便地查尋和訪問其中所包含的磁盤塊&#xff1b;2. 文件&#xff1a;文件系統中存儲數據的一個命名的對象。3. 目錄&#xff1a;其中包含許多文件…

JDK源碼解析之 java.lang.Class

Java程序在運行時&#xff0c;Java運行時系統一直對所有的對象進行所謂的運行時類型標識。 這項信息紀錄了每個對象所屬的類。虛擬機通常使用運行時類型信息選準正確方法去執行&#xff0c;用來保存這些類型信息的類是Class類。Class類封裝一個對象和接口運行時的狀態&#xff…