【Java并發(fā)學(xué)習(xí)】之詳解線程的點滴(1)
前言
在前面的幾個小節(jié)中盔沫,我們粗略地學(xué)習(xí)了線程的基本概念,定義任務(wù)的方式,并且通過Java中的多線程API來驅(qū)動對應(yīng)的任務(wù)漱办,然后學(xué)習(xí)了線程的互斥與同步關(guān)系苇羡,線程之間進(jìn)行資源共享后的同步操作等等一些簡單的內(nèi)容绸吸,可以說鼻弧,線程中的主要內(nèi)容我們已經(jīng)掌握了,然而锦茁,也僅僅只是簡單內(nèi)容攘轩,很多比較細(xì)節(jié)、復(fù)雜的東西在之前我們刻意避開了码俩,接下來的幾個小節(jié)中度帮,我們就來具體地學(xué)習(xí)線程的各個細(xì)節(jié)內(nèi)容
線程的屬性
線程有許多的屬性,比如線程的ID稿存、名字笨篷、優(yōu)先級、狀態(tài)等瓣履,接下來我們就來具體學(xué)習(xí)這些屬性
線程的ID
每個線程都有對應(yīng)的獨一無二的ID率翅,這些ID是由JVM在運行的時候為線程設(shè)置的,用于區(qū)分不同的線程袖迎,并且不對外提供設(shè)置線程ID的接口冕臭,也就是說,在Java中燕锥,我們僅能夠獲取多線程的ID辜贵,而不能設(shè)置線程的ID,訪問代碼如下
class DetailTask implements Runnable{
@Override
public void run() {
System.out.println("Details : " + Thread.currentThread().getId());
// 其中Thread.currentThread()用于獲取當(dāng)前執(zhí)行的線程
// 通過thread.getId()則可以獲取對應(yīng)的線程的ID
}
}
線程的名字
在線程比較多的情況下归形,我們會傾向于根據(jù)執(zhí)行不同任務(wù)的不同線程來為其設(shè)置名字托慨,方便在日志以及其他場景中使用到,設(shè)置以及獲取線程的名字操作具體如下
class DetailTask implements Runnable{
private static int cnt = 0;
// 注意這里的代碼是非線程安全的
@Override
public void run() {
Thread.currentThread().setName("Thread " + cnt++); // 設(shè)置線程名字
System.out.println("Details : " + Thread.currentThread().getName()); // 獲取線程的名字
}
}
線程優(yōu)先級
優(yōu)先級是一個比較有價值的概念暇榴,在Java中厚棵,線程之間的調(diào)度采用的是基于時間片的輪轉(zhuǎn)調(diào)度方式,其中在線程進(jìn)行切換跺撼,重新調(diào)度時窟感,調(diào)度的主要依據(jù)就是優(yōu)先級,通常來說歉井,優(yōu)先級比較高的線程柿祈,被調(diào)度的可能性會比較大,也就是說哩至,在單位時間內(nèi)躏嚎,優(yōu)先級高的線程被調(diào)度的次數(shù)會大于優(yōu)先級低的線程
不過,在設(shè)置線程的優(yōu)先級的時候菩貌,需要注意一下幾點
- 線程的有效優(yōu)先級僅能為 0 - 10 之間的整數(shù)卢佣,其他數(shù)字則會拋出
java.lang.IllegalArgumentException
- 由于不同的操作系統(tǒng)的優(yōu)先級的范圍不一致,而映射到0-10之間則會出現(xiàn)一些意想不到的驚喜箭阶,所以一般在設(shè)置優(yōu)先級的時候虚茶,不能過分依賴于優(yōu)先級戈鲁,盡量只使用
Thread.MAX_PRIORITY
、Thread.MIN_PRIORITY
以及默認(rèn)的優(yōu)先級嘹叫,分別對應(yīng)的數(shù)值為10,0,1 - 設(shè)置優(yōu)先級僅僅僅能提高線程被調(diào)度的概率婆殿,也就是僅能提高線程被調(diào)度的次數(shù),但是并不能保證執(zhí)行次數(shù)一定會高罩扇,所以婆芦,不能僅依賴優(yōu)先級來實現(xiàn)如同步、合作之類的操作
- 優(yōu)先級的設(shè)置必須在線程啟動之前設(shè)置喂饥,也就是必須在thread.start()方法執(zhí)行之前進(jìn)行設(shè)置消约,否則,會采用默認(rèn)的優(yōu)先級员帮,言下之意即不能在run方法中進(jìn)行優(yōu)先級的設(shè)置
for (int i = 0; i < 10; i++){
Thread thread = new Thread(task);
System.out.println("Priority " + thread.getPriority());
if (i % 2 == 0){
thread.setPriority(Thread.MIN_PRIORITY); // 設(shè)置優(yōu)先級為最低
}else {
thread.setPriority(Thread.MAX_PRIORITY); // 設(shè)置優(yōu)先級為最高
}
thread.start();
}
線程的狀態(tài)
通常來講或粮,線程的數(shù)量是多于CPU的數(shù)量,這也就意味著集侯,為了保證每個線程都能得到執(zhí)行被啼,在特定時刻,總會有線程處于非運行狀態(tài)棠枉,當(dāng)然,Java中線程的狀態(tài)不止這兩種泡挺,具體如下
-
NEW
:新建狀態(tài)辈讶,此時線程已經(jīng)被賦予了任務(wù)锦秒,但需要注意的是哈雏,此時執(zhí)行該線程所需要的資源仍未準(zhǔn)備好,是不具備執(zhí)行條件的寡键,也就是在調(diào)用start()方法之前的狀態(tài) -
RUNNABLE
:可運行狀態(tài)媳溺,表明此時該線程已經(jīng)是處于可執(zhí)行的狀態(tài)月幌,這里需要注意的是,可運行狀態(tài)可以是該線程正在執(zhí)行悬蔽,也可能該線程的除了CPU之外的其他資源都已經(jīng)準(zhǔn)備完畢扯躺,只要分配給其CPU就能執(zhí)行。注意是Runnable
而不是Run
-
BLOCKED
:阻塞狀態(tài)蝎困,線程如果處于該狀態(tài)录语,則表明此時該線程正在等在某個鎖的釋放 -
WAITING
:等待狀態(tài),表明此時該線程正在等待某些其他線程的資源禾乘,通常是調(diào)用了Object.wait()
澎埠、join()
、LockSupport.park()
這幾個方法 -
TIMED_WAITING
:等待超時狀態(tài)始藕,同樣是等待狀態(tài)蒲稳,只是多了個計時器氮趋,通常是調(diào)用了上面幾個方法中帶時間參數(shù)的重載方法 -
TERMINATED
:中斷/結(jié)束狀態(tài),表明此時該線程已經(jīng)處于執(zhí)行完畢的狀態(tài)或者是被其他線程或者JVM中斷江耀,結(jié)束其運行
關(guān)于這幾個狀態(tài)的詳細(xì)介紹凭峡,可以參考JDK源碼中的注釋
/* 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;
}
守護(hù)線程
上面我們主要學(xué)習(xí)了線程的幾個屬性,ID决记、名字摧冀、優(yōu)先級、狀態(tài)等系宫,接下來我們來學(xué)習(xí)一個新的概念索昂,守護(hù)線程
守護(hù)線程是一類特殊的線程,主要的特點如下
- 被調(diào)度的頻率較低
- 當(dāng)當(dāng)前系統(tǒng)中的所有非守護(hù)線程結(jié)束時扩借,守護(hù)線程也會結(jié)束椒惨,而且不管正在執(zhí)行什么任務(wù)
基于守護(hù)線程的特點,在使用的時候需要注意潮罪,不能在守護(hù)線程中做一些邏輯比較復(fù)雜的操作康谆,不能做一些比較重要的操作
一個典型的使用守護(hù)線程的例子就是JVM中的垃圾回收器
public static void main(String[] args) {
Thread daemonTask = new Thread(new DaemonTask());
daemonTask.setDaemon(true);// 設(shè)置為守護(hù)線程,這里同樣需要注意嫉到,必須在線程啟動之前進(jìn)行設(shè)置
daemonTask.start();
}
class DaemonTask implements Runnable{
@Override
public void run() {
while (true){
System.out.println("I'm Daemon thread");
}
}
}
總結(jié)
本小計我們主要學(xué)習(xí)了線程的相關(guān)屬性沃暗,包括了ID、名字何恶、優(yōu)先級孽锥、狀態(tài),以及守護(hù)線程的概念和設(shè)置守護(hù)線程的方法细层,線程中的異常處理惜辑,接下來我們將學(xué)習(xí)線程的中斷以及線程的異常處理等等的內(nèi)容