zoukankan      html  css  js  c++  java
  • 【Java Concurrency】sleep()、wait()、notify()、notifyAll()的用法与区别

    >关于本文

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

    1. sleep()和wait()的区别。
    2. wait()与notify()、notifyAll()之前互相协调的关系。
    3. notify()与notifyAll()的区别。

    > Thread.sleep(long),睡眠指定时间

    此方法是让线程睡眠指定时间不释放锁(睡觉,当然要上锁,这个还用说么)。

    此方法我貌似很少用,又似乎很常用。因为,在正式代码中我很少用到,而在测试代码中,却又经常用来模拟某某业务需时几秒的阻塞。

    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...");
    
        }
    
    }

    一般,还有个更易读的写法,一样的效果

    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(),等待(条件)

    线程转到等待状态释放锁。(既然等待,当然得释放锁了,我们在等待、迎接贵宾时,也是敞开着大门的,哈哈)

    持有锁的情况下才能调用此方法,通常搭配外层的循环以判断是否继续等待。

    wait方法可以执行时间,也可不,由notify方法唤醒。

     

    晚餐、客人、服务员的例子

    /**
     * 晚餐
     */
    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

    日志:

    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编程思想》,机械工业出版社

  • 相关阅读:
    php array function
    scrum敏捷开发重点介绍
    PHP文件操作
    正则
    PHP面向对象
    PHP数组
    PHP函数参数
    PHP运算符优先级
    PHP判断变量类型和类型转换的三种方式
    PHP变量的传值和引用
  • 原文地址:https://www.cnblogs.com/nick-huang/p/4787571.html
Copyright © 2011-2022 走看看