首頁  >  文章  >  Java  >  Java執行緒的6種狀態與生命週期是什麼

Java執行緒的6種狀態與生命週期是什麼

王林
王林轉載
2023-05-02 12:07:061431瀏覽

    1.執行緒狀態(生命週期)

    一個執行緒在給定的時間點只能處於一種狀態。

    執行緒可以有以下6 種狀態:

    • New (新建立):未啟動的執行緒;

    • Runnable (可運行):可運行的線程,需要等待作業系統資源;

    • Blocked (被阻塞):等待監視器鎖定而被阻塞的執行緒;

    • #Waiting (等待):等待喚醒狀態,無限期地等待另一個執行緒喚醒;

    • #Timed waiting (計時等待):在指定的等待時間內等待另一個執行緒執行操作的執行緒;

    • Terminated (被終止):已退出的執行緒。

    要確定一個執行緒的目前狀態,可呼叫getState 方法

    #執行緒狀態關係圖

    注意:虛線框(全大寫英文)的狀態為Java線程狀態。

    Java執行緒的6種狀態與生命週期是什麼

    2.操作執行緒狀態

    2.1.新建立狀態(NEW)

    就是實例化執行緒完成後,未啟動執行緒的狀態。

    可透過三種方式建立執行緒

    • 重寫Thread類別run()方法

    • 實作Runnable介面

    • 實作Callable介面

    一個簡單的範例概括三種方式

    public class Demo {
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            /**
             * 1.直接重写run() 或继承Thread类再重写run()
             */
            Thread thread = new Thread() {
                @Override
                public void run() {
                    System.out.println("Thread");
                }
            };
            // 开启线程
            thread.start();
    
            /**
             * 2.lambda、内部类或线程类方式实现Runnable接口,实现run()方法
             * 再交给Thread 类
             */
            Thread runThread = new Thread(() -> {
                System.out.println("Runnable");
            });
            // 开启线程
            runThread.start();
    
            /**
             * 3.lambda、内部类或线程类方式实现Callable接口,实现call()方法
             * 再交给Thread 类:FutureTask本质也是Runnable实现类
             */
            FutureTask<String> futureTask = new FutureTask<String>(() -> {
                System.out.println("Callable");
                return "CallableThread";
            });
            Thread callThread = new Thread(futureTask);
            // 开启线程
            callThread.start();
            // 获取call()方法的返回值
            String s = futureTask.get();
            System.out.println("call()方法的返回值:"+s);
        }
    
    }

    不重寫run() 或call()方法直接實例化Thread類別所建立的執行緒沒有實際意義;

    只有Callable方式所建立的執行緒可以取得執行緒的回傳值。

    2.2.可運行狀態(RUNNABLE)

    該狀態指的是執行緒實例化物件呼叫start()方法後進入的狀態。執行緒處於可以運作狀態,如果有處理器等資源,就可以執行程式。

    該狀態在作業系統層面包含兩個步驟:執行緒就緒和執行緒執行中,但在Java執行緒狀態中,這兩個步驟都統稱為Runnable(可執行)狀態。

    線程由就緒狀態變成運行狀態,重點就看你的線程有沒有搶到CPU資源(CPU時間片),誰搶到就運行,沒搶到就等。因為CPU時間片(執行時間)非常短,大​​概十幾毫秒,所以線程切換的這個時間是非常短的,就緒狀態變成運行狀態的時間也非常短,在開發時幾乎感覺不到這種狀態的變化,所以在Java中將兩者看作是一個整體,重點關注線程可否運行並區別於其他狀態即可,更進一步簡化線程的開發。如果你的程式要運行很久(例如寫個死循環),在一個CPU時間片內沒有執行完成,那麼你的線程就要搶下一次的CPU時間片,搶到了才可以繼續執行程序,沒搶到那就要繼續搶,直到線程中的程式執行完成。

    其實這個場景應該都見過,例如多個執行緒執行同一個程序,都將日誌印到同一個檔案時,就會出現不同執行緒的日誌混在了一起的情況,不利於排查問題。解決這種問題常見的方法有:一是分線程列印日誌到不同檔案;二是將日誌資訊儲存到字串物件中,在程式的最後將日誌資訊一次列印到檔案。第二種方式就是利用CPU的一個時間片來完成日誌資訊的列印。

    注意:程式只能對新建狀態的執行緒呼叫start()方法,不要對處於非新建狀態的執行緒呼叫start() 方法,這都會引發IllegalThreadStateException例外。

    2.3.被阻塞狀態(BLOCKED)

    執行緒處於等待監視器鎖定而被阻塞的狀態。有一個線程獲取了鎖未釋放,其他線程也來獲取,但發現獲取不到鎖也進入了被阻塞狀態。

    被阻塞狀態只存在於多執行緒並發存取下,區別於後面兩種因執行緒自己進入」等待「而導致的阻塞。

    進入狀態

    • 進入synchronized 程式碼區塊/方法

    • 未取得到鎖定

    退出狀態

    • 取得到監視器鎖定

    2.4.等待喚醒狀態(WAITING)

    整個流程是這樣的:執行緒在某個物件的同步方法中先取得到物件鎖定;在執行wait方法時,該執行緒將釋放物件鎖定,並且該執行緒被放入到這個物件的等待佇列;等待另一個執行緒取得到同一個物件的鎖,然後透過notify() 或notifyAll() 方法喚醒物件等待佇列中的執行緒。

    從整個流程可以知道

    wait (),notify () 和notifyAll () 方法需要在執行緒取得到鎖的情況下才可以繼續執行,所以這三個方法都需要放在同步程式碼區塊/方法中執行,否則報異常:java.lang.IllegalMonitorStateException。

    在同步代码块中,线程进入WAITING 状态时,锁会被释放,不会导致该线程阻塞。反过来想下,如果锁没释放,那其他线程就没办法获取锁,也就没办法唤醒它。

    进入状态

    • object.wait()

    • thread.join()

    • LockSupport.park()

    退出状态

    • object.notify()

    • object.notifyall()

    • LockSupport.unpark()

    2.5.计时等待状态(TIMED_WAITING)

    一般是计时结束就会自动唤醒线程继续执行后面的程序,对于Object.wait(long) 方法还可以主动通知唤醒。

    注意:Thread类下的sleep() 方法可以放在任意地方执行;而wait(long) 方法和wait() 方法一样,需要放在同步代码块/方法中执行,否则报异常:java.lang.IllegalMonitorStateException。

    进入状态

    • Thread.sleep(long)

    • Object.wait(long)

    • Thread.join(long)

    • LockSupport.parkNanos(long)

    • LockSupport.parkNanos(Object blocker, long nanos)

    • LockSupport.parkUntil(long)

    • LockSupport.parkUntil(Object blocker, long deadline)

    注:blocker 参数为负责此线程驻留的同步对象。

    退出状态

    • 计时结束

    • LockSupport.unpark(Thread)

    • object.notify()

    • object.notifyall()

    2.6.终止(TERMINATED)

    线程执行结束

    • run()/call() 执行完成

    • stop()线程

    • 错误或异常>>意外死亡

    stop() 方法已弃用。

    3.查看线程的6种状态

    通过一个简单的例子来查看线程出现的6种状态。

    案例

    public class Demo3 {
        private static Object object ="obj";
        
        public static void main(String[] args) throws InterruptedException {
    
            Thread thread0 = new Thread(() -> {
                try {
                    // 被阻塞状态(BLOCKED)
                    synchronized (object){
                        System.out.println("thread0 进入:等待唤醒状态(WAITING)");
                        object.wait();
                        System.out.println("thread0 被解除完成:等待唤醒状态(WAITING)");
                    }
                    System.out.println("thread0 "+Thread.currentThread().getState());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            // 新创建状态(NEW)
            System.out.println(thread0.getName()+":"+thread0.getState());
    
            Thread thread1 = new Thread(() -> {
                try {
                    System.out.println("thread1 进入:计时等待状态(TIMED_WAITING)");
                    Thread.sleep(2);
                    System.out.println("thread1 出来:计时等待状态(TIMED_WAITING)");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 被阻塞状态(BLOCKED)
                synchronized (object){
                    System.out.println("thread1 解除:等待唤醒状态(WAITING)");
                    object.notify();
                    System.out.println("thread1 解除完成:等待唤醒状态(WAITING)");
                }
                System.out.println("thread1 "+Thread.currentThread().getState());
            });
            // 新创建状态(NEW)
            System.out.println(thread1.getName()+":"+thread1.getState());
    
            printState(thread0);
            printState(thread1);
    
            // 可运行状态(RUNNABLE)
            thread0.start();
            // 可运行状态(RUNNABLE)
            thread1.start();
    
        }
        
        
        // 使用独立线程来打印线程状态
        private static void printState(Thread thread) {
            new Thread(()->{
                while (true){
                    System.out.println(thread.getName()+":"+thread.getState());
                    if (thread.getState().equals(Thread.State.TERMINATED)){
                        System.out.println(thread.getName()+":"+thread.getState());
                        break;
                    }
                }
            }).start();
        }
    }

    执行结果:简化后的输出结果

    Thread-0:NEW
    Thread-1:NEW
    Thread-0:RUNNABLE
    Thread-1:RUNNABLE
    thread0 进入:等待唤醒状态(WAITING)
    Thread-1:BLOCKED
    thread1 进入:计时等待状态(TIMED_WAITING)
    Thread-0:BLOCKED
    Thread-0:WAITING
    ……
    Thread-0:WAITING
    Thread-1:BLOCKED
    Thread-1:TIMED_WAITING
    ……
    Thread-1:TIMED_WAITING
    Thread-1:BLOCKED
    ……
    Thread-1:BLOCKED
    Thread-0:WAITING
    ……
    Thread-0:WAITING
    thread1 出来:计时等待状态(TIMED_WAITING)
    Thread-0:WAITING
    Thread-1:BLOCKED
    thread1 解除:等待唤醒状态(WAITING)
    Thread-1:BLOCKED
    Thread-0:WAITING
    Thread-0:BLOCKED
    thread1 解除完成:等待唤醒状态(WAITING)
    Thread-1:BLOCKED
    thread1 RUNNABLE
    Thread-0:BLOCKED
    Thread-1:TERMINATED
    thread0 被解除完成:等待唤醒状态(WAITING)
    Thread-0:BLOCKED
    thread0 RUNNABLE
    Thread-0:TERMINATED

    Java執行緒的6種狀態與生命週期是什麼

    最终的执行结果如图。

    注意:因为案例中使用了独立线程来打印不同线程的状态,会出现状态打印稍微延迟的情况。

    以上是Java執行緒的6種狀態與生命週期是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除