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

12 篇文章 0 订阅

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 exit
Task name:Thread-A; exexute time interval = 10000

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值