0
点赞
收藏
分享

微信扫一扫

【多线程篇】sleep和wait的区别?notify和notify的作用?如何实现生产者-消费者模式

49路末班车 2022-03-11 阅读 28
java

目录

sleep和wait的区别?

1. sleep方法(休眠)

1. 源码分析

class Thread implements Runnable {

	public static native void sleep(long millis) throws InterruptedException;
}

sleep方法Thread类的实例方法,且这个方法被native修饰,是一个本地库接口

2. 作用

让线程进入阻塞状态,并主动让出CPU,但还会占有锁

3. 题外话:sleep之后线程会释放锁吗

不会,sleep方法只对CPU操作,并不会去操作对象的锁(tips:对象锁的状态是保存在对象头的,这个在【八股文】JVM篇有讲

4. sleep的使用场景

高优先级线程因为一些原因(自身操作比较耗时、需要低优先级线程的数据),需要让低优先级线程先执行,可以自己调用sleep操作,让出CPU

2. wait方法(等待)

1. 源码分析

public class Object {

    public final void wait() throws InterruptedException {
        wait(0);
    }
    public final native void wait(long timeout) throws InterruptedException;

}

wait方法Object对象类的实例方法,是对象的方法,其本质也是用native修饰的,是一个本地库方法

1. 作用

自己进入等待池暂时(暂时体现在哪里?和sleep区别?)让出CPU的使用权,也会释放同步锁

2. 使用场景

  1. 生产者-消费者模式

3. notify

1. notify的作用

  1. 等待池中的线程转移到锁池中(锁池中的线程会去积极地抢锁)
  2. notify方法不释放锁,要等方法执行完毕之后才会释放锁

2. 为什么要使用notify、notifyAll?

因为要提醒其他线程,从等待池锁池

例如:
线程A调用wait方法之后,会进入等待池线程A会把这个锁释放,供其他线程抢,线程B抢到这个锁,并跑完程序,想要释放这个锁给线程A用,但是线程A还在等待池中,这时候就必须要使用notifynotifyAll方法,把线程A叫到锁池

3. 应用场景

  1. 生产者-消费者模式

4. 如何使用wait和notifyAll实现生产者-消费者模式

1. 注意事项

  1. 使用waitnotify可以实现线程间的通信(多个线程可以使用这些方法相互通信)
  2. 必须获得锁才能使用这些方法,如synchronizedsynchronized块里使用
  3. while代替if做条件判断
  4. notifyAll代替notify

1. 题外话:为什么wait和notify方法要在synchronized方法里使用?

synchronized的语义底层是通过一个monitor的对象来完成,其实wait、notify等方法也依赖于monitor对象,否则会抛出java.lang.IllegalMonitorStateException的异常(不合法的监视器状态)

2. 实现

1. 生产者

public class Producer implements Runnable {

    private final List<Integer> container;
    public static final int MAX_CAPACITY = 5;

    public Producer(List<Integer> container) {
        this.container = container;
    }

    private void produce() throws InterruptedException {

        synchronized (container) {
            while (container.size() == MAX_CAPACITY) {
                System.out.println("容器已满,暂停生产");
                container.wait();
            }
            int p = RandomUtils.nextInt();
            System.out.println("生产产品:" + p);
            container.add(p);
            container.notifyAll();
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

1. 消费者

public class Consumer implements Runnable{

    private List<Integer> container;

    public Consumer(List<Integer> container) {
        this.container = container;
    }

    private void consume() throws InterruptedException {
        synchronized (container){
            while (container.isEmpty()){
                System.out.println("容器已空,暂停消费");
                container.wait();
            }
            
            Integer p = container.remove(0);
            TimeUnit.MILLISECONDS.sleep(1000);
            System.out.println("消费产品:" + p);
            container.notifyAll();
        }
    }
    @Override
    public void run() {
        while (true){
            try {
                consume();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3. 生产者消费者测试

public class ProducerConsumerTest {
    public static void main(String[] args) {
        List<Integer> container = new ArrayList<>();
        Thread producer = new Thread(new Producer(container));
        Thread consumer = new Thread(new Consumer(container));
        producer.start();
        consumer.start();
    }
}

4. 控制台现象

Connected to the target VM, address: '127.0.0.1:54861', transport: 'socket'
容器已空,暂停消费
生产产品:1539478251
生产产品:2055287902
生产产品:244175613
生产产品:836101091
生产产品:346467918
容器已满,暂停生产
消费产品:1539478251
消费产品:2055287902
消费产品:244175613
消费产品:836101091
消费产品:346467918
容器已空,暂停消费
生产产品:2056187818
生产产品:1148572189
生产产品:1951629848
生产产品:767221295
生产产品:2009198874
容器已满,暂停生产
1. 控制台现象分析

消费者发现容器为空,调用wait方法直接去等待池,生产者开始生产数据,生产者满了将调用wait,并notifyAll其他线程,消费者被唤醒,开始消费数据。。。如此往复执行。。

2. 问:为什么会一直循环下去?start启动一个线程之后,不会关闭吗?

如果代码运行完毕的话,是会关闭线程的,一直循环说明进入了while,编不下去了。。TODO搞不清,不太理解

5. 为什么wait方法可以释放对象的锁,而sleep不行?

  • wait方法Object的方法(对象的方法),那就代表着wait方法可以控制对象相关的一些属性,比如对象头中的一些属性(如对象锁的状态持有锁的线程名),所以wait方法可以释放对象的锁
  • sleep方法Thread的方法,不能控制对象相关的操作,也就不能控制锁的释放了

6. sleep线程是怎么让出CPU的?原理是什么

sleep方法是native修饰的,代表是本地库接口,原理是其他语言实现的,我也不清楚

举报

相关推荐

0 条评论