進程 & 線程的概念:
進程:在某種程度上表示相互隔離的、獨立運行的程序。
線程:也稱作輕量級進程。就象進程一樣,線程在程序中是獨立的、并發的執行路徑,每個線程有它自己的堆棧、自己的程序計數器和自己的局部變量。
進程 & 線程的區別:
與分隔的進程相比,進程中的線程之間的隔離程度要小。
進程可以支持多個線程,它們看似同時執行,但互相之間并不同步。一個進程中的多個線程共享相同的內存地址空間,這就意味著它們可以訪問相同的變量和對象,而且它們從同一堆中分配對象。
在 java 中,用 Thread 類表示線程。
在 Thtead 的內部有個枚舉類,它定義了線程的狀態:
public enum State { NEW, // 新建狀態 RUNNABLE, // 就緒狀態(即可執行狀態) BLOCKED, // 阻塞狀態 WAITING, // 等待狀態 TIMED_WAITING, // 等待狀態(有時間限制) TERMINATED; // 死亡狀態}NEW ,指線程剛創建, 尚未啟動。
RUNNABLE ,表示線程可以正常參與競爭 CPU 資源,成功率與其優先級高低有關。
BLOCKED,表示線程進入阻塞并監視鎖的狀態。
它存在于多個線程有同步操作的場景。比如正在等待另一個線程的 synchronized 塊的執行釋放。
WAITING ,該狀態下的線程表示正在等待另外一個線程的動作。
例如:
調用了 wait 方法且沒有超時,進入等待狀態,等待另一個線程調用 notify/notiyfAll 方法。調用了 join 方法且沒有超時 ,進入等待狀態,等待另一個線程終止。調用了 LockSupport.park ,進入等待狀態,等待另一個線程調用 LockSupport.park。TIMED_WAITING ,存在于:
調用了 wait / wait(long) 方法且沒有超時 調用了 join 方法且沒有超時調用了 LockSupport.parkNanos/LockSupport.parkUntil 方法線程的優先級具有以下特點:
優先級代表獲取 CPU 資源的概率,優先級高的線程獲取的概率較大。
優先級無法保證線程的執行次序,因為這個一個概率問題。
線程的優先級用1-10之間的整數表示,數值越大優先級越高,默認的優先級為5。
在一個線程中開啟另外一個新線程,則新開線程稱為該線程的子線程,子線程初始優先級與父線程相同。
在 Thhtead 類中,定義了三個優先級常量:
// 最小public final static int MIN_PRIORITY = 1;// 默認public final static int NORM_PRIORITY = 5;// 最大public final static int MAX_PRIORITY = 10;在 Java 中,用 Thread 表示線程類,它實現了 Runnbale 接口。
首先來看它的簽名:
// 簽名public class Thread implements Runnable// 接口public interface Runnable { public abstract void run();}再來看它的部分構造函數:
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0);}public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0);}public Thread(String name) { init(null, null, name, 0);}public Thread(Runnable target, String name) { init(null, target, name, 0);}private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null);}// 關鍵private void init(ThreadGroup g, Runnable target, String name, long stackSize, accessControlContext acc){...}從代碼可知,構建線程的真正過程在 init 方法中實現。構建一個線程所需要的必要參數有:
ThreadGroup :線程組,表示一個線程的集合。此外,線程組也可以包含其他線程組。線程組構成一棵樹,在樹中,除了初始線程組外,每個線程組都有一個父線程組。
target:目標,即運行內容,可通過 run 方法執行。
name:線程名稱,默認是 Thread-id,每個線程都不有不同的線程名。
stackSize:開辟新增線程所需的??臻g,為 0 表示忽略該參數。
AccessControlContext :上下文。
構建一個線程后,可以通過 start 方法來啟動它,同時也表示線程進入就緒狀態。
同樣的,也可以同調用 Thread 的 yeid 方法來已經運行線程重新進入就緒狀態。
yield
public static native void yield();start
// 線程狀態,默認為 0,表示線程還未啟動,即 NEW 狀態private volatile int threadStatus = 0;// 表示線程的線程組private ThreadGroup group;public synchronized void start() { if (threadStatus != 0){ //拋出異常... } // 加入線程組 group.add(this); boolean started = false; try { // 關鍵 start0(); started = true; } finally { try { // 啟動失敗,通知線程組 if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { // do nothing... } }}// 使用了本地調用,通過 C 代碼初始化線程需要的系統資源。private native void start0();線程在啟動之后,會自動調用 run 方法,執行規定好的內容。
private Runnable target;public void run() { if (target != null) { target.run(); }}由代碼可知,線程在啟動后會自動執行 Runnable.run 定義的內容。
線程中斷,并不是指中斷線程的運行,而是指設置線程的中斷標記位。
在 Thread 類中,有三個方法跟中斷標記位有關:
public static void main(String[] args) { // interrupt:設置線程的中斷狀態 Thread.currentThread().interrupt(); // interrupted:返回線程的上次的中斷狀態,并清除中斷狀態 System.out.println(Thread.currentThread().interrupted()); // isInterrupt:返回線程的上次的中斷狀態 System.out.println(Thread.currentThread().isInterrupted());}下面來看它們的具體實現:
interrupt,設置線程的中斷狀態// 用于同步,表示鎖對象private final Object blockerLock = new Object();private volatile Interruptible blocker;public void interrupt() { if (this != Thread.currentThread()){ // 檢查系統訪問權限 checkAccess(); } synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { // 關鍵 -> 調用本地方法,設置線程中斷標志位 interrupt0(); b.interrupt(this); return; } } interrupt0();}// 設置線程中斷標志位private native void interrupt0();若想要令線程進入阻塞狀態,可以調用 Thread 類的 sleep 方法實現。
public static void sleep(long millis, int nanos) throws InterruptedException { if (millis < 0) { // 拋出異常... } if (nanos < 0 || nanos > 999999) { // 拋出異常... } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } sleep(millis);}public static native void sleep(long millis) throws InterruptedException;在 Thread 類中,想要讓線程進入等待,可以調用 join 方法實現。
Thread t1 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); System.out.println("t1 完成工作"); } catch (InterruptedException e) { e.printStackTrace(); } }});t1.start();// 關鍵把 t1 線程加入到當前線程,等待 t1 執行結束,main 才能繼續執行t1.join();System.out.println("main 完成工作");下面再來看看它的具體實現:
public final void join() throws InterruptedException { join(0);}public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { // 拋出異常... } if (millis == 0) { // 關鍵 -> 線程(指代例子中 t1)存活,則(main)進入等待 while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } }}// 判斷線程是否存活public final native boolean isAlive();這里同樣有個疑問,既然 mian 進入等待,那么在 t1 結束后如何被喚醒?
正常情況下進入 wait 的 線程,需要其他線程調用 notify/notifyAll 來喚醒。答案在 t1 線程結束后在 JVM 中會調用 notify 方法:
void JavaThread::exit(bool destroy_vm, ExitType exit_type) ;// 確保 join 函數ensure_join(this);static void ensure_join(JavaThread* thread) { Handle threadObj(thread, thread->threadObj()); ObjectLocker lock(threadObj, thread); thread->clear_pending_exception(); java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); java_lang_Thread::set_thread(threadObj(), NULL); // 關鍵 -> 調用線程的 notifyAll 方法。 lock.notify_all(thread); thread->clear_pending_exception();}新聞熱點
疑難解答