Java多线程系列4(wait/notify/notifyAll)

来源:互联网 发布:数字青少年宫网络寄语 编辑:程序博客网 时间:2024/06/05 00:48

  • waitnotifynotifyAll
  • 实现生产者消费者模型
  • 继续理解waitnotifynotifyAll

1 wait/notify/notifyAll

这三个方法是在java.lang.Object类提供的,使用的时候需要注意:
(1)这三个方法需要在synchronized方法或者同步快中调用;
(2)调用wait/notify/notifyAll的对象必须和synchronized对应的对象一致。也即如果synchronized(variableA),但是却使用variableB.wait()是不行的。
如果不遵循上面两个前提条件,会报错:
java.lang.IllegalMonitorStateException

wait:会导致当前线程进行等待,交出监视器的控制权,直到其他线程调用监视器的notify/notifyAll
notify:唤醒在监视器上等待的单个线程
notifyAll:唤醒在监视器上等待的所有线程

2 实现生产者/消费者模型

以下是使用wait/notify来实现的经典的生产者/消费者的代码示例:

public class test {    public static class Product {        private int maxSize;        private List<String> productList = new ArrayList<>();        public Product() {            this.maxSize = 10;        }        public void produceProduct() {            synchronized (this) {                while (productList.size() == maxSize) {                    try {                        wait();                    } catch (InterruptedException e) {                    }                }                System.out.println("Produce product, Num = " + productList.size());                productList.add(productList.size() + "");                notify();            }        }        public void consumeProduct() {            synchronized (this) {                while (productList.size() == 0) {                    try {                        wait();                    } catch (InterruptedException e) {                    }                }                System.out.println("Consume product, Num = " + productList.get(productList.size() - 1));                productList.remove(productList.size() -1);                notify();            }        }    }    public static class ConsumerThread implements Runnable {        private Product product;        public ConsumerThread(Product product) {            this.product = product;        }        @Override        public void run() {            for (int i = 0; i < 50; i++) {                try {                    Thread.sleep(100);                } catch (InterruptedException e) {                }                product.consumeProduct();            }        }    }    public static class ProduceThread implements Runnable {        private Product product;        public ProduceThread(Product product) {            this.product = product;        }        @Override        public void run() {            for (int i = 0; i < 50; i++) {                try {                    Thread.sleep(10);                } catch (InterruptedException e) {                }                product.produceProduct();            }        }    }    public static void main(String[] args) {        Product product = new Product();        Thread consumer = new Thread(new ConsumerThread(product));        Thread producer = new Thread(new ProduceThread(product));        consumer.start();        producer.start();        try {            consumer.join();            producer.join();        } catch (InterruptedException e) {        }        System.out.println("main therad exit");    }}

总结:
1)以上代码中使用Product的实例作为监视器,实际使用中也可以使用productList,或者使用新的变量Object lock = new Object(),都可以;
2)调用wait的对象,必须和监视器一样;
3)实现生产者/消费者模型时,执行wait/notify前提条件,一般都会写在 while循环中,这是为了防止,出现 InteruptedException时,条件还没有满足,就执行下面的任务了。
以上是实现生产者/消费者模型时,需要注意的问题。

3 继续理解wait/notify/notifyAll

在实际使用中,如果线程A调用wait进入Blocked状态,线程B在synchronized块(或者方法)中调用notify后,必须等到整个synchronized执行完毕以后,才会释放synchronized对象锁,这样后续线程A才能获得synchronized对象锁由Blocked状态变为Runnable,然后在wait点之后继续执行。
继续思考如下场景,能够更加透彻的理解
1)wait/notify:
基于同一个对象作为synchronized对象,如果有A/B/C三个线程调用了wait,进入阻塞状态。然后线程D调用notify,根据系统的调度,A/B/C中只会有一个线程会随机收到通知,收到通知的线程会被添加到synchronized锁对象的锁池中,在这个锁池中的线程会竞争synchronized锁,竞争到锁的线程会被移出锁池,继续执行wait之后的代码。
2)wait/notifyAll
和上面一样,唯一的差别是,当线程D调用notifyAll时,A/B/C三个线程都会收到通知,都会被添加到synchronized锁对象的锁池中,这个锁池中的线程会竞争synchronized锁,竞争到的锁的线程会被移出锁池,继续执行wait之后的代码。假设A竞争到锁,此时会执行A线程中wait之后的代码,执行完毕以后,释放锁以后,B/C继续继续竞锁,直到锁池中的线程都获得锁,执行完毕代码。

可见,当notify/notifyAll调用以后,wait不一定会立即执行,必须在调用notify/notifyAll的线程释放锁以后,wait的线程才有机会得到执行。
设计下面的线程便可看到效果:

public class test {    private static Object lock = new Object();    private static long time = System.currentTimeMillis();    public static class Task extends Thread {        public Task(String name) {            super(name);        }        @Override        public void run() {            synchronized (lock) {                try {                    lock.wait();                } catch (InterruptedException e) {                }                System.out.println("Task name:" + getName() + "; exexute time interval = " + (System.currentTimeMillis() - time));            }        }    }    public static void main(String[] args) {        Thread a = new Task("Thread-A");        Thread b = new Task("Thread-B");        Thread c = new Task("Thread-C");        a.start();        b.start();        c.start();        //main线程sleep 1s,等待县城A/B/C都调用wait方法等待        try {            Thread.sleep(1000);        } catch (InterruptedException e) {        }        time = System.currentTimeMillis();        synchronized (lock) {            lock.notify();            //main线程执行notify之后,延迟10s才退出同步块,释放lock            try {                Thread.sleep(10000);            } catch (InterruptedException e) {            }        }        System.out.println("main therad exit");    }}

执行结果:

main therad exitTask name:Thread-A; exexute time interval = 10000

可以看到main线程执行notify以后,延迟10s才释放锁,A/B/C中A在10s以后才执行wait,这也证明了我们的分析。

原创粉丝点击