【Java併發學習】之詳解線程的點滴(1)
前言
在前面的幾個小節中,我們粗略地學習了線程的基本概念,定義任務的方式,並且通過Java中的多線程API來驅動對應的任務,然後學習了線程的互斥與同步關係,線程之間進行資源共享後的同步操作等等一些簡單的內容,可以說,線程中的主要內容我們已經掌握了,然而,也僅僅只是簡單內容,很多比較細節、複雜的東西在之前我們刻意避開了,接下來的幾個小節中,我們就來具體地學習線程的各個細節內容
線程的屬性
線程有許多的屬性,比如線程的ID、名字、優先級、狀態等,接下來我們就來具體學習這些屬性
線程的ID
每個線程都有對應的獨一無二的ID,這些ID是由JVM在運行的時候爲線程設置的,用於區分不同的線程,並且不對外提供設置線程ID的接口,也就是說,在Java中,我們僅能夠獲取多線程的ID,而不能設置線程的ID,訪問代碼如下
class DetailTask implements Runnable{
@Override
public void run() {
System.out.println("Details : " + Thread.currentThread().getId());
// 其中Thread.currentThread()用於獲取當前執行的線程
// 通過thread.getId()則可以獲取對應的線程的ID
}
}
線程的名字
在線程比較多的情況下,我們會傾向於根據執行不同任務的不同線程來爲其設置名字,方便在日誌以及其他場景中使用到,設置以及獲取線程的名字操作具體如下
class DetailTask implements Runnable{
private static int cnt = 0;
// 注意這裏的代碼是非線程安全的
@Override
public void run() {
Thread.currentThread().setName("Thread " + cnt++); // 設置線程名字
System.out.println("Details : " + Thread.currentThread().getName()); // 獲取線程的名字
}
}
線程優先級
優先級是一個比較有價值的概念,在Java中,線程之間的調度採用的是基於時間片的輪轉調度方式,其中在線程進行切換,重新調度時,調度的主要依據就是優先級,通常來說,優先級比較高的線程,被調度的可能性會比較大,也就是說,在單位時間內,優先級高的線程被調度的次數會大於優先級低的線程
不過,在設置線程的優先級的時候,需要注意一下幾點
- 線程的有效優先級僅能爲 0 - 10 之間的整數,其他數字則會拋出
java.lang.IllegalArgumentException
- 由於不同的操作系統的優先級的範圍不一致,而映射到0-10之間則會出現一些意想不到的驚喜,所以一般在設置優先級的時候,不能過分依賴於優先級,儘量只使用
Thread.MAX_PRIORITY
、Thread.MIN_PRIORITY
以及默認的優先級,分別對應的數值爲10,0,1 - 設置優先級僅僅僅能提高線程被調度的概率,也就是僅能提高線程被調度的次數,但是並不能保證執行次數一定會高,所以,不能僅依賴優先級來實現如同步、合作之類的操作
- 優先級的設置必須在線程啓動之前設置,也就是必須在thread.start()方法執行之前進行設置,否則,會採用默認的優先級,言下之意即不能在run方法中進行優先級的設置
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); // 設置優先級爲最低
}else {
thread.setPriority(Thread.MAX_PRIORITY); // 設置優先級爲最高
}
thread.start();
}
線程的狀態
通常來講,線程的數量是多於CPU的數量,這也就意味着,爲了保證每個線程都能得到執行,在特定時刻,總會有線程處於非運行狀態,當然,Java中線程的狀態不止這兩種,具體如下
NEW
:新建狀態,此時線程已經被賦予了任務,但需要注意的是,此時執行該線程所需要的資源仍未準備好,是不具備執行條件的,也就是在調用start()方法之前的狀態RUNNABLE
:可運行狀態,表明此時該線程已經是處於可執行的狀態,這裏需要注意的是,可運行狀態可以是該線程正在執行,也可能該線程的除了CPU之外的其他資源都已經準備完畢,只要分配給其CPU就能執行。注意是Runnable
而不是Run
BLOCKED
:阻塞狀態,線程如果處於該狀態,則表明此時該線程正在等在某個鎖的釋放WAITING
:等待狀態,表明此時該線程正在等待某些其他線程的資源,通常是調用了Object.wait()
、join()
、LockSupport.park()
這幾個方法TIMED_WAITING
:等待超時狀態,同樣是等待狀態,只是多了個計時器,通常是調用了上面幾個方法中帶時間參數的重載方法TERMINATED
:中斷/結束狀態,表明此時該線程已經處於執行完畢的狀態或者是被其他線程或者JVM中斷,結束其運行
關於這幾個狀態的詳細介紹,可以參考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;
}
守護線程
上面我們主要學習了線程的幾個屬性,ID、名字、優先級、狀態等,接下來我們來學習一個新的概念,守護線程
守護線程是一類特殊的線程,主要的特點如下
- 被調度的頻率較低
- 噹噹前系統中的所有非守護線程結束時,守護線程也會結束,而且不管正在執行什麼任務
基於守護線程的特點,在使用的時候需要注意,不能在守護線程中做一些邏輯比較複雜的操作,不能做一些比較重要的操作
一個典型的使用守護線程的例子就是JVM中的垃圾回收器
public static void main(String[] args) {
Thread daemonTask = new Thread(new DaemonTask());
daemonTask.setDaemon(true);// 設置爲守護線程,這裏同樣需要注意,必須在線程啓動之前進行設置
daemonTask.start();
}
class DaemonTask implements Runnable{
@Override
public void run() {
while (true){
System.out.println("I'm Daemon thread");
}
}
}
總結
本小計我們主要學習了線程的相關屬性,包括了ID、名字、優先級、狀態,以及守護線程的概念和設置守護線程的方法,線程中的異常處理,接下來我們將學習線程的中斷以及線程的異常處理等等的內容