【Java Concurrency】sleep()、wait()、notify()、notifyAll()的用法與區別

>關於本文

本文介紹sleep()、wait()、notify()、notifyAll()方法,主要要理解:html

  1. sleep()和wait()的區別。
  2. wait()與notify()、notifyAll()以前互相協調的關係。
  3. notify()與notifyAll()的區別。

 

> Thread.sleep(long),睡眠指定時間

此方法是讓線程睡眠指定時間不釋放鎖(睡覺,固然要上鎖,這個還用說麼)。java

此方法我貌似不多用,又彷佛很經常使用。由於,在正式代碼中我不多用到,而在測試代碼中,卻又常常用來模擬某某業務需時幾秒的阻塞。編程

public class Sleep {

    public static void main(String[] args) {
        
        System.out.println("Start...");
        
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        System.out.println("End...");

    }

}

 

通常,還有個更易讀的寫法,同樣的效果api

import java.util.concurrent.TimeUnit;


public class Sleep {

    public static void main(String[] args) {
        
        System.out.println("Start...");
        
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        System.out.println("End...");

    }

}

 

> Object.wait(),等待(條件)

線程轉到等待狀態釋放鎖。(既然等待,固然得釋放鎖了,咱們在等待、迎接貴賓時,也是敞開着大門的,哈哈)oracle

持有鎖的狀況下才能調用此方法,一般搭配外層的循環以判斷是否繼續等待。ide

wait方法能夠執行時間,也可不,由notify方法喚醒。測試

 

 

晚餐、客人、服務員的例子this

/**
 * 晚餐
 */
public class Dinner {
    
    private String mainDish; // 主菜

    public String getMainDish() {
        return mainDish;
    }

    public void setMainDish(String mainDish) {
        this.mainDish = mainDish;
    }
    
}
View Code

 

/**
 * 客人
 */
public class Customer extends Thread {
    
    private Dinner d;
    
    public Customer(Dinner d) {
        super();
        this.d = d;
    }

    @Override
    public void run() {
        synchronized (d) {
            while (d == null || d.getMainDish() == null) {
                try {
                    System.out.println(currentThread().getName() + ", customer start to wait.");
                    d.wait();
                    System.out.println(currentThread().getName() + ", customer end to wait.");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            
            d.setMainDish(null); // 至關於把菜吃掉
            System.out.println(currentThread().getName() + ", customer eat the food.");
        }
        super.run();
    }
    
}
View Code

 

/**
 * 服務員
 */
public class Waiter extends Thread {
    
    private Dinner d;
    
    public Waiter(Dinner d) {
        super();
        this.d = d;
    }

    @Override
    public void run() {
        synchronized (d) {
            d.notify();
            d.setMainDish("牛扒"); // 至關於上菜
            System.out.println(currentThread().getName() + ", waiter notify.");
        }
        super.run();
    }

}
View Code

 

import java.util.concurrent.TimeUnit;


public class HowToUse {

    public static void main(String[] args) {
        Dinner d = new Dinner();
        Customer c = new Customer(d);
        Waiter w = new Waiter(d);
        
        c.start();
        
        /* 等待一段時間,目的讓Customer線程先啓動 */
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        w.start();
    }

}
View Code

 

日誌:spa

Thread-0, customer start to wait.
Thread-1, waiter notify.
Thread-0, customer end to wait.
Thread-0, customer eat the food.

 

固然,也可執行時長中止等待了:線程

public class Wait {

    public static void main(String[] args) {
        
        System.out.println("Start...");
        
        String s = "";
        synchronized (s) {
            try {
                s.wait(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        System.out.println("End...");

    }

}
View Code

 

> Object.notify()和Object.notifyAll(),喚醒等待的線程

一個是通知一個線程(通知哪個線程是不肯定的哦),另外一個是喚醒所有線程

 

將HowToUse方法修改下:

import java.util.concurrent.TimeUnit;


public class HowToUse {

    public static void main(String[] args) {
        Dinner d = new Dinner();
        Customer c1 = new Customer(d);
        Customer c2 = new Customer(d);
        Waiter w = new Waiter(d);
        
        c1.start();
        c2.start();
        
        /* 等待一段時間,目的讓Customer線程先啓動 */
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        w.start();
    }

}
View Code

就能夠看到:

Thread-0, customer start to wait.
Thread-1, customer start to wait.
Thread-2, waiter notify.
Thread-0, customer end to wait.
Thread-0, customer eat the food.

Thread-0、Thread-1分別表明兩個客人,waiter喚醒了Thread-0,屬於Thread-0的客人中止等待,去吃大餐了。

 

將notify修改爲notifyAll:

/**
 * 服務員
 */
public class Waiter extends Thread {
    
    private Dinner d;
    
    public Waiter(Dinner d) {
        super();
        this.d = d;
    }

    @Override
    public void run() {
        synchronized (d) {
            d.notifyAll();
            d.setMainDish("牛扒"); // 至關於上菜
            System.out.println(currentThread().getName() + ", waiter notify.");
        }
        super.run();
    }

}
View Code

能夠看到日誌:

Thread-0, customer start to wait.
Thread-1, customer start to wait.
Thread-2, waiter notify.
Thread-1, customer end to wait.
Thread-1, customer eat the food.
Thread-0, customer end to wait.
Thread-0, customer start to wait.

Thread-2通知你們後,Thread-0、Thread-1都中止等待了,只不過Thread-1搶到了食物,吃完了,因此Thread-0又得從新等待。

 

> 何時用notify,何時用notifyAll?

若是各線程等待的條件不同,那麼要用notifyAll,由於用notify只通知到一個線程,而那線程的不知足跳出等待的條件,那麼不就很差了嗎。

好比,有一位富有的客人,要求晚餐的主菜中要有紅酒才知足,與普通客人等待的條件不同,若是服務員只准備了牛扒沒有紅酒,而有用notify只通知了富有的客人,那麼普通的客人就沒被通知到了。

/**
 * 富有的客人
 */
public class WealthyCustomer extends Thread {
    
    private Dinner d;
    
    public WealthyCustomer(Dinner d) {
        super();
        this.d = d;
    }

    @Override
    public void run() {
        synchronized (d) {
            while (d == null || d.getMainDish() == null || !d.getMainDish().contains("紅酒")) { // 富有的客人,要求的主菜要有紅酒
                try {
                    System.out.println(currentThread().getName() + ", wealthy customer start to wait.");
                    d.wait();
                    System.out.println(currentThread().getName() + ", wealthy customer end to wait.");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            
            d.setMainDish(null); // 至關於把菜吃掉
            System.out.println(currentThread().getName() + ", wealthy customer eat the food.");
        }
        super.run();
    }
    
}
View Code

 

import java.util.concurrent.TimeUnit;


public class HowToUse {

    public static void main(String[] args) {
        Dinner d = new Dinner();
        Customer c1 = new Customer(d);
        WealthyCustomer c2 = new WealthyCustomer(d);
        Waiter w = new Waiter(d);
        
        c2.start(); // 將WealthyCustomer的線程的先啓動,比較容易看到Waiter通知他
        c1.start();
        
        /* 等待一段時間,目的讓Customer線程先啓動 */
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        w.start();
    }

}
View Code

 

將Waiter設置成notify,會看到以下日誌:

Thread-1, wealthy customer start to wait.
Thread-0, customer start to wait.
Thread-2, waiter notify.
Thread-1, wealthy customer end to wait.
Thread-1, wealthy customer start to wait.

 

將Waiter設置成notifyAll,日誌以下:

Thread-1, wealthy customer start to wait.
Thread-0, customer start to wait.
Thread-2, waiter notify.
Thread-0, customer end to wait.
Thread-0, customer eat the food.
Thread-1, wealthy customer end to wait.
Thread-1, wealthy customer start to wait.

 

> 參考的優秀文章

Thread.sleep(long millis) API doc

wait(long timeout) API doc

notify() API doc

notifyAll() API doc

《Java編程思想》,機械工業出版社

相關文章
相關標籤/搜索