Java線程的wait(), notify()和notifyAll()


Java線程生命周期

類java.lang.Thread包含一個靜態的State enum用於定義每種可能的狀態. 在任意的時間點, 線程會處於以下的狀態之一:

NEW – 新創建的線程, 還未啟動(在調用 start() 之前的狀態). A thread that has not yet started is in this state.
RUNNABLE – 正在運行或已經准備好運行但是在等待資源. either running or ready for execution but it’s waiting for resource allocation
BLOCKED – 在等待獲取一個monitor lock來進入或重新進入同步的代碼塊或方法 waiting to acquire a monitor lock to enter or re-enter a synchronized block/method
WAITING – 在等待其他線程執行一個特定的操作, 沒有時間限制 waiting for some other thread to perform a particular action without any time limit
由這三種方法之一進入 
object.wait()
thread.join()
LockSupport.park()

TIMED_WAITING – 在某個時間限制內, 等待其他線程執行一個特定的操作 waiting for some other thread to perform a specific action for a specified period
由這五種方法之一進入 
thread.sleep(long millis)
wait(int timeout) or wait(int timeout, int nanos)
thread.join(long millis)
LockSupport.parkNanos
LockSupport.parkUnti

TERMINATED – 運行已經結束或意外終止 has completed its execution

 

wait(), notify()和notifyAll()方法用於在線程間建立關聯. 在對象上調用wait()將使線程進入WAITTING狀態, 直到其他線程對同一個對象調用notify()或notifyAll(). 在任何線程上, 對一個對象調用wait(), notify()和notifyAll(), 都需要先獲得這個對象的鎖, 就是說, 這些方法必須在synchronized方法或代碼塊中調用.

wait()

在對象上調用wait(), 會使當前線程進入等待狀態, 直至另一個線程對這個對象調用了notify() 或notifyAll() 方法. 換句話說, 就是簡單的執行了wait(0)方法.

在調用wait()時, 當前線程必須擁有這個對象的monitor. 調用后, 線程會釋放對象的monitor, 並一直等待直到另一個線程通過notify()或notifyAll()喚醒正在等待這個對象的monitor的線程們. 然后線程會一直等待直到重新拿回這個對象的monitor, 然后才能繼續運行. 

對於帶參數的版本, 中斷和喚醒都是可能的, 這個方法必須在一個循環中使用.

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }

.

notify()

喚醒正在等待這個對象的monitor的線程中的一個. 如果有多個線程正在等待, 會選擇其中的一個, 這個選擇是任意的, 因具體實現而不同. 喚醒的線程並不會立即繼續執行, 它需要獲得這個對象的鎖之后才能繼續執行. 喚醒的對象將與其他正在競爭這個對象的鎖的線程一起競爭對象的鎖. 

這個方法只允許在擁有當前對象的monitor的線程上調用, 一個線程要得到對象的monitor, 只能通過以下三種方法之一:

  • 執行這個對象的一個同步方法
  • 執行這個對象中的一段同步代碼
  • 對於type類型的對象, 執行這個類的一個靜態同步方法

在同一時刻, 只允許一個線程得到一個對象的monitor

調用notify()時, 在所有WAITING狀態的線程中只會有一個線程被通知, 這個選擇是隨機的, 被通知的線程並不會立即得到對象的鎖, 而是一直等到調用notify()的線程釋放鎖, 在這之前線程都是BLOCKED狀態. 當獲得鎖后, 就會從BLOCKED狀態變為RUNNING狀態. 例子

class Shared {
    synchronized void waitMethod() {
        Thread t = Thread.currentThread();
        System.out.println(t.getName() + " is releasing the lock and going to wait");
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.getName() + " has been notified and acquired the lock back");
    }

    synchronized void notifyOneThread() {
        Thread t = Thread.currentThread();
        notify();
        System.out.println(t.getName() + " has notified one thread waiting for this object lock");
    }
}

public class MainClass {
    public static void main(String[] args) {
        final Shared s = new Shared();
        //Thread t1 will be waiting for lock of object 's'
        Thread t1 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t1.start();

        //Thread t2 will be waiting for lock of object 's'
        Thread t2 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t2.start();

        //Thread t3 will be waiting for lock of object 's'
        Thread t3 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t3.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Thread t4 will notify only one thread which is waiting for lock of object 's'
        Thread t4 = new Thread() {
            @Override
            public void run() {
                s.notifyOneThread();
            }
        };
        t4.start();
    }
}

.

notifyAll()

當線程在對象上調用notifyAll()時, 所有WAITING狀態的線程都會被喚醒, 所有的線程都會從WAITING狀態變成BLOCKED狀態, 然后爭搶對象的鎖. 得到對象鎖的線程, 將變成RUNNING狀態, 而其他線程則繼續保持BLOCKED狀態繼續等待獲取對象鎖. 例子

class Shared {
    synchronized void waitMethod() {
        Thread t = Thread.currentThread();
        System.out.println(t.getName() + " is releasing the lock and going to wait");
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.getName() + " has been notified and acquired the lock back");
    }

    synchronized void notifyAllThread() {
        Thread t = Thread.currentThread();
        notifyAll();
        System.out.println(t.getName() + " has notified all threads waiting for this object lock");
    }
}

public class MainClass {
    public static void main(String[] args) {
        final Shared s = new Shared();
        //Thread t1 will be waiting for lock of object 's'
        Thread t1 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t1.start();

        //Thread t2 will be waiting for lock of object 's'
        Thread t2 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t2.start();

        //Thread t3 will be waiting for lock of object 's'
        Thread t3 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t3.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Thread t4 will notify all threads which are waiting for lock of object 's'
        Thread t4 = new Thread() {
            @Override
            public void run() {
                s.notifyAllThread();
            }
        };
        t4.start();
    }
}

.

一個生產者和消費者的例子

注意, 在1個生產1個消費的情況下, 是能確保生產和消費的互相通知的, 但是在2個生產1個消費的情況下, 有可能要多次notify后消費線程才能拿到queue的鎖.

public class DemoThreadWait1 {
    Queue<Integer> queue = new LinkedList<>();

    public void consume() {
        synchronized (queue) {
            while (queue.isEmpty()) {
                try {
                    System.out.println("consume wait");
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("remove all");
                queue.clear();
                queue.notify();
            }
        }
    }

    public void produce(int i) {
        synchronized (queue) {
            if (queue.size() < 5) {
                System.out.println("add " + i);
                queue.add(i);
            }
            if (queue.size() >= 5) {
                queue.notify();
                try {
                    System.out.println("produce wait");
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        DemoThreadWait1 demo = new DemoThreadWait1();
        new Thread(()->{
            while(true) {
                demo.consume();
            }
        }).start();

        new Thread(()->{
            while(true) {
                demo.produce((int) (Math.random() * 1000));
            }
        }).start();

        new Thread(()->{
            while(true) {
                demo.produce((int) (Math.random() * 1000));
            }
        }).start();
    }
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM