Skip to main content

等待与唤醒与锁

threadthread等待与唤醒About 2 min

主要介绍一下wait notify, park unpark 等的使用, 做一些例子供参考

wait与notify

wait和notify是Object类中的native方法, 且不可以重写. 在执行的时候, 依赖一个对象的监视器. 因此我们需要使用同步, 确保其能获取到对象的锁.
注意, wait之后不能没有notify, 当然也可以指定等待时间.

下面的例子是通过wait和notify实现的生产消费功能, 当生产数量达到一定值的时候唤醒消费线程生产等待, 消费到0唤醒生产线程消费等待.

public class TestWaitNotify {

    private static Object obj = new Object();
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            SleepUtil.sleepSecond(1);
            synchronized (obj) {
                while (true) {
                    SleepUtil.sleepSecond(1);
                    if (count >= 5) {
                        obj.notify();
                        System.out.println("consumer notify");
                        try {
                            System.out.println("producer wait");
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("producer: " + ++count);
                }
            }
        }, "生产").start();


        new Thread(() -> {
            synchronized (obj) {
                while (true) {
                    if (count == 0) {
                        obj.notify();
                        System.out.println("producer notify");
                        try {
                            System.out.println("consumer wait");
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                    SleepUtil.sleepSecond(1);
                    System.out.println("consumer used: " + --count);

                }
            }
        }, "消费").start();

    }
}

执行结果:

2020-05-04-17-01-00

park和unpark

LockSupport的park和unpark是基于AQS实现的, 线程在默认情况下, 是不持有许可证的, 当park方法执行的时候会被阻塞. 执行unpark, 线程获取到许可, 因此park直接返回.
下面也是一个生产消费的例子.

public class TestParkUnpark {

    private static Thread producer = new ProducerThread("生产");
    private static Thread consumer = new ConsumerThread("消费");

    private static volatile int count = 0;

    public static void main(String[] args) {
        producer.start();
        consumer.start();
    }

    private static class ProducerThread extends Thread {

        public ProducerThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                SleepUtil.sleepSecond(2);
                System.out.println("producer: " + ++count);
                if (count >= 5) {
                    LockSupport.unpark(consumer);
                    LockSupport.park();
                }
            }
        }
    }

    private static class ConsumerThread extends Thread {

        public ConsumerThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                SleepUtil.sleepSecond(1);
                if (count <= 0) {
                    LockSupport.unpark(producer);
                    LockSupport.park();
                }
                System.out.println("consumer: " + --count);
            }
        }
    }
}


运行结果:

2020-05-04-17-39-01

比较

  1. unpark可以指定需要唤醒的线程, 而notify是随机唤醒, notifyAll全部唤醒, 无法唤醒指定的线程.
  2. wait和notify是需要对象锁的, 而park unpark是基于AQS实现的
  3. 两者都有可能产生死锁, 尤其是最后执行wait或者park, 因此建议有循环唤醒的机制.
What do you think?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v3.0.0-alpha.10