Thread的幾種狀態以及sleep/yield/join/wait/notify/notifyAll方法的區別

線程狀態

Java的Thread類中對線程的狀態有如下定義:

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

NEW

Thread state for a thread which has not yet started.

尚未啓動的線程的線程狀態。

此時線程剛剛創建,並未調用start()方法啓動。

Thread thread = new Thread();
System.out.println(thread.getState()); // NEW

RUNNABLE

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.

可運行線程的線程狀態。 處於可運行狀態的線程正在Java虛擬機中執行,但它可能正在等待來自操作系統的其他資源,例如處理器。

線程調用start()方法開始運行,這個狀態下也會發生一些等待,一般等待的是其他系統資源,而不是鎖、sleep等。

Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getState())); // RUNNABLE
thread.start();

BLOCKED

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

線程的線程狀態被阻塞等待監視器鎖定。 處於阻塞狀態的線程正在等待監視器鎖定以在調用{@link Object#wait()Object.wait}之後輸入同步塊/方法或重新輸入同步塊/方法。

這種狀態是在有多個線程有同步操作的場景,如下thread1雖然釋放了CPU,但是沒有釋放people的鎖,致使thread2進入了阻塞狀態。

Thread thread1 = new Thread(() -> {
    synchronized (people) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
thread1.start();

Thread thread2 = new Thread(() -> {
    synchronized (people) {

    }
});
thread2.start();

WAITING

Thread state for a waiting thread. A thread is in the waiting state due to calling one of the following methods:

{@link Object#wait() Object.wait} with no timeout

{@link #join() Thread.join} with no timeout

{@link LockSupport#park() LockSupport.park}

A thread in the waiting state is waiting for another thread to perform a particular action.

For example, a thread that has called Object.wait() on an object is waiting for another thread to call Object.notify() or Object.notifyAll() on that object. A thread that has called Thread.join() is waiting for a specified thread to terminate.

等待線程的線程狀態。 由於調用以下方法之一,線程處於等待狀態:

Object.wait()

Thread.join()

LockSupport.park()

處於等待狀態的線程正在等待另一個線程執行特定操作。

例如,在對象上調用Object.wait()的線程正在等待另一個線程調用 Object.notify()或Object.notifyAll()。 調用Thread.join()的線程正在等待指定的線程終止。

該狀態下的線程處於等待狀態,當另一線程喚醒這個線程時,線程立馬進入就緒狀態。

Thread thread1 = new Thread(() -> {
    synchronized (people) {
        try {
            people.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
thread1.start();

TIMED_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:

{@link #sleep Thread.sleep}

{@link Object#wait(long) Object.wait} with timeout

{@link #join(long) Thread.join} with timeout

{@link LockSupport#parkNanos LockSupport.parkNanos}

{@link LockSupport#parkUntil LockSupport.parkUntil}

具有指定等待時間的等待線程的線程狀態。 由於在指定的正等待時間內調用以下方法之一,線程處於定時等待狀態:

Thread.sleep

Object.wait(long)

Thread.join(long)

LockSupport.parkNanos

LockSupport.parkUtil

該狀態的線程需要等待一些時間才能繼續運行。WAITING狀態是當發生某個事情纔會進入就緒狀態,TIMED_WAITING狀態是當時間到達就會進入就緒狀態。

Thread thread1 = new Thread(() -> {
    synchronized (people) {
        try {
            people.wait(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
thread1.start();

TERMINATED

Thread state for a terminated thread. The thread has completed execution.

終止線程的線程狀態。 線程已完成執行。

run()方法執行結束後,線程進入TERMINATED狀態。

主要方法

wait()

wait()方法是Object的方法,是在資源的角度阻塞線程,也就是讓線程放棄對資源的佔用。

public static void main(String[] args) {
    People people = new People();

    // 線程一拿到對象的鎖
    Thread thread1 = new Thread(() -> {
       synchronized (people) {
           System.out.println(1);
           try {
               // 對象調用wait方法使線程進入WAITING狀態
 			   // people的鎖被釋放
               people.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println(2);
       }
    });
    thread1.start();

    // 線程二運行,拿到對象的鎖
    Thread thread2 = new Thread(() -> {
       synchronized (people) {
           System.out.println(3);
       }
    });
    thread2.start();
}

運行結果:

1
3

因爲線程一依舊處於阻塞狀態,所以主線程main此時並沒有結束運行。

sleep()

sleep()方法是線程的方法,是在線程的角度阻塞線程,無法操作對象鎖,所以如果線程阻塞時鎖住了某個對象,那麼這個對象的鎖將不會被釋放。

public static void main(String[] args) {
    People people = new People();

    Thread thread1 = new Thread(() -> {
       synchronized (people) {
           System.out.println(1);
           try {
               // 線程調用sleep方法阻塞線程,線程進入TIMED_WAITING
               // 對象的鎖沒有被釋放
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println(2);
       }
    });
    thread1.start();

    // 線程二拿不到people的鎖所以不能執行進入就緒狀態
    Thread thread2 = new Thread(() -> {
       synchronized (people) {
           System.out.println(3);
       }
    });
    thread2.start();
}

運行結果:

1
2
3

兩者的區別:

sleep() wait()
所屬類不同 Thread類的方法 Object類的方法
時間不同 必須指定時間 可指定也可不指定時間
釋放鎖不同 釋放CPU執行權不釋放鎖 釋放CPU執行權釋放鎖
使用範圍不同 可以在任意地方使用 只能在同步代碼中使用

notify()

參考Java 源碼–Object

notify()和notifyAll()方法喚醒被wait()方法置於WAITING狀態的線程。notify()方法喚醒一個等待資源的線程,如果有多個線程同時等待一個資源,則由JVM選擇一個線程喚醒,使其進入就緒狀態,重新競爭鎖。

public static void main(String[] args) {
    People people = new People();

    Thread thread1 = new Thread(() -> {
        synchronized (people) {
            try {
                System.out.println(1);
                // thread1進入WAITING狀態
                // 釋放鎖
                people.wait();
                System.out.println(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    thread1.start();

    Thread thread2 = new Thread(() -> {
        synchronized (people) {
            try {
                System.out.println(3);
                people.wait();
                System.out.println(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    thread2.start();

    Thread thread3 = new Thread(() -> {
        synchronized (people) {
            System.out.println(5);
            // 喚醒一個等待people鎖的線程
            people.notify();
            System.out.println(6);
        }
    });
    thread3.start();
}

運行結果如下,thread2被喚醒,thread1依舊處於WAITING狀態。

1
3
5
6
4

notifyAll()

notifyAll()方法喚醒所有等待資源的線程。

public static void main(String[] args) {
    People people = new People();

    Thread thread1 = new Thread(() -> {
        synchronized (people) {
            try {
                System.out.println(1);
                people.wait();
                System.out.println(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    thread1.start();

    Thread thread2 = new Thread(() -> {
        synchronized (people) {
            try {
                System.out.println(3);
                people.wait();
                System.out.println(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    thread2.start();

    Thread thread3 = new Thread(() -> {
        synchronized (people) {
            System.out.println(5);
            // 喚醒所有等待people鎖的線程
            people.notifyAll();
            System.out.println(6);
        }
    });
    thread3.start();
}

運行結果如下,thread1和thread2均被喚醒。

1
3
5
6
4
2

yield()

yield()方法把CPU讓給同等優先級或更高優先級的線程。注意,其他線程不會立馬進入運行狀態,只是給其他線程提供競爭的機會。

public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
        System.out.println(1);
        Thread.yield();
        System.out.println(2);
    });
    thread1.setPriority(Thread.MIN_PRIORITY);
    thread1.start();

    Thread thread2 = new Thread(() -> System.out.println(3));
    thread2.setPriority(Thread.MAX_PRIORITY);
    thread2.start();
}

運行結果:

1
2
3

1
3
2

thread1禮讓之後,thread1和thread2一起參與競爭,都有可能進入運行狀態。疑惑?此處thread2的優先級已被設爲最高了,爲什麼不一定是thread2先運行呢?

join()

join()方法會使當前線程等待調用join()方法的線程結束後才能繼續執行。

public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
        try {
            Thread.sleep(10000);
            System.out.println("我剛睡醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    thread1.start();

    Thread thread2 = new Thread(() -> {
        try {
            System.out.println(1);
            thread1.join();
            System.out.println(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    thread2.start();
}

運行結果:

1
我剛睡醒
2
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章