Java多线程之 - wait,notify,notifyall作用、用法

在这里插入图片描述

  • 阻塞阶段
    执行wait 要获得这个对象的monitor 锁 ,调用wait后线程就处于阻塞状态了,直到以下四种之一情况发生,才会被唤醒.
    • 另一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程;
    • 另一个线程调用这个对象的notifyAll()方法;
    • 过了wait(long timeout)规定的超时时间,如果传入0就是永久等待;
    • 线程自身调用了interrupt()
  • 唤醒阶段
    notify唤醒单个正在等待某对象monitor的线程,有多个线程在等待会随机唤醒一个线程nofy得在synchronized保护块内部运行,如若在外部会抛出异常。如果获得锁的线程调用了notifyall则会唤醒所有等待线程然后随机有个线程将会获得锁。
  • 遇到中断
    抛出异常,释放当前获得的monitor。

模拟 当线程wait 之后 其他线程使用 notify 唤醒线程。

public class Wait{
    public static Object object = new Object();
    static class Thread1 extends Thread{
        @Override
        public  void run(){
            synchronized (object){
                System.out.println("线程1开始执行了");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1" +Thread.currentThread().getName() +"获取到了锁");
            }

        }

    }
    static class Thread2 extends Thread{
        @Override
        public  void run(){
            synchronized (object){

                object.notify();
                System.out.println("线程2" +Thread.currentThread().getName() +"调用了notify()");
            }

        }

    }
    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1=  new Thread1();
        Thread2 thread2=  new Thread2();
        thread1.start();
        Thread.sleep(200);
        thread2.start();
    }

}

西面的代码模仿 线程 A 和 线程B 先后获取锁 然后wait()释放锁 然后线程C再去唤醒线程A和线程B 继续执行 .

public class WaitNotifyAll implements  Runnable {
    private static  Object resourceA = new Object();

    public static void main(String[] args) throws InterruptedException {
        Runnable r = new WaitNotifyAll();
        Thread threadA = new Thread(r);
        Thread threadB = new Thread(r);
        Thread threadC = new Thread(new Runnable() {
            public void run() {
                synchronized (resourceA){
                    resourceA.notifyAll();
                    System.out.println("Thread notified");

                }
            }
        });
        threadA.start();
        threadB.start();
        Thread.sleep(1000);
        threadC.start();


    }
    public void run() {
        synchronized (resourceA){
            System.out.println(Thread.currentThread().getName() + "got resourceA lock");
            try {
                System.out.println(Thread.currentThread().getName() + "waits to start");
                resourceA.wait();
                System.out.println(Thread.currentThread().getName() + "is waiting to end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }
}

有A锁和B锁 先是获取了A锁 然后获取了B锁 然后释放了A锁 然后第二个线程可以拿到A锁 但是B锁并没有被释放

public class WaitNotifyReleaseOwnMonitor {
    private static volatile Object resourceA = new Object();
    private static volatile  Object resourceB = new Object();

    public static void main(String[] args) {
        Thread threada = new Thread(new Runnable() {
            public void run() {
                synchronized (resourceA){
                    System.out.println("Thread A got ResourceA lock");
                    synchronized (resourceB){
                        System.out.println("ThreadA got resourceB lock");
                        try{
                            resourceA.wait();
                            System.out.println("ThreadA releases resourceA lock.");
                        } catch (InterruptedException e) {
                            e.printStackTrace();

                        }
                    }
                }
            }
        });
        Thread threadb = new Thread(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resourceA){
                    System.out.println("ThreadB got resourceA lock");
                    synchronized (resourceB){
                        System.out.println("ThreadB got resourceB lock");
                    }
                }

            }
        });

        threada.start();
        threadb.start();
    }
}

在这里插入图片描述

wait,notify,notifyall特点、性质

  • 用前必须拥有monitor
  • notify只能唤醒其中一个由取决于JVM
  • 属于Object类
  • 类似功能的Condition
  • 同时持有多个锁的情况

手写生产者消费者设计模式

  • 为什么使用生产者和消费者模式
    在线程中 有的线程是生产出数据 有的是消费数据 但两者之间速度是不一致的 如果有一个设计模式能解决他们之间相互等待的问题 而不至于一个过快一个过慢,相互配合比较困难 将消费方 和生产方 之间解耦 以便于更加好的配合。

在这里插入图片描述
生产者 把生产出来的数据放入 中间的阻塞队列 消费者再通过阻塞队列去取 这样一来就有个缓冲区这样就是把他们的能力进行平衡 ,当生产者生产数据过多时阻塞消费者消化,等到差不多是再提醒生产者继续生产 。
在这里插入图片描述

消费者 生产者模式代码 实现 2个线程 一个生产者 一个消费者 轮流获得锁 去生产和消费轮流进行。

public class ProducerConsumerModel {
    public static void main(String[] args) {
        EventStorage eventStorage =new EventStorage();
        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);
        Thread threada =  new Thread(producer);
        Thread threadb =new Thread(consumer);
        threada.start();
        threadb.start();
    }

}
    class Consumer implements Runnable {
        private EventStorage<Integer>  storge;
        public Consumer(EventStorage storage){
            this.storge = storage;

        }
        public void run() {
            for(int i=0;i<100;i++){
                storge.take();

            }
        }

    }
     class Producer implements Runnable {
        private EventStorage<Integer>  storge;
        public Producer(EventStorage storage){
            this.storge = storage;
        }
        public void run() {
            for(int i=0;i<100;i++){
                storge.put(i);

                }
            }
        }


    class EventStorage<E>{
        private int maxSize;
        private LinkedList<E> storage;
        public EventStorage(){
            maxSize = 10;
            storage = new LinkedList<E>();
        }
        public synchronized void put(E e){
            while(storage.size() ==maxSize){
                try {
                    wait();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
            storage.add(e);
            System.out.println("仓库里有了" +storage.size() +"个产品.");
            notify();
        }
        public synchronized void take() {
            while (storage.size() == 0) {
                try {
                    //notify(); 如果写在这 就是当消费光了再去生产

                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("仓库拿到了" + storage.poll() + "剩余:"+storage.size());
            //每次消费了就去生产
            notify();
            }

        }

wait,notify,notifyAll常见问题

两个线程交替打印 0~100 的奇偶性

public class WaitNotifyPrint0ddEvenSyn implements Runnable {
    int i=0;

    public static void main(String[] args) throws InterruptedException {
        WaitNotifyPrint0ddEvenSyn waitNotifyPrint0ddEvenSyn = new WaitNotifyPrint0ddEvenSyn();
        Thread thread1 = new Thread(waitNotifyPrint0ddEvenSyn,"偶数线程");
        Thread thread2 = new Thread(waitNotifyPrint0ddEvenSyn,"奇数线程");
        thread1.start();
        Thread.sleep(1000);
        thread2.start();
    }
    public void run() {
        synchronized (this){
            while(i<100){
                if((i & 1) == 0){
                    System.out.println(  Thread.currentThread().getName()+"打印奇数" + i );
                    i++;
                    notify();
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{

                    System.out.println( Thread.currentThread().getName()+"打印偶数" + i);
                    i++;
                    notify();
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            notifyAll();
        }
    }
}

在这里插入图片描述

  • 为什么wait()需要在同步代码块内使用,而sleep不需要
    • 通信变得可靠,防止死锁发生 假设设计是先要执行wait()之后执行notify()唤醒 但如果wait()没有同步代码块保护 随时可能被其他线程切换过去执行notify 等其他线程执行完 notify 线程 再执行wait 这样这个线程就永远会等待下去了. 每人去唤醒它了.slee()是针对单独线程的不影响其他线程所以不需要放到同步代码块中。
  • 为什么线程通信的方法wait(),notify()和notifyall()被定义在Object类里?而sleep定义在Thread类里?
    • wait(),notify()和notifyall() 是锁级别的操作 锁属于某个对象的 每一个对象的对象头都有过几个字段来保存锁的状态的 所以锁是和对象绑定的 而不是和 某个线程绑定。如果把所定义在线程里 那么逻辑不够灵活很多情况下 需要1个线程 可以拥有多把锁配合 就像1个人可以拥有好几把锁 配合开不同的门。
  • wait方法是属于Object对象的 ,那调用Thread.wait 会怎么样?
    • Thread 也是一个继承与Object的对象,但Thread 会自动的调用notify流程会受到干扰,故不要用Thread作为锁。
  • 如何选择用notify ()还是 notifyAll ()?
    • 考虑到现实情况 需要唤醒一个 还是多个
  • notifyAll 之后所有的线程都会再次抢夺锁,如果其他线程抢夺失败怎么办?
    • 没抢到的会回到最初始的状态,等待抢到锁。
  • suspend()resume() 来阻塞线程可以吗?为什么?
    • 已经不推荐了,改用notyify() 和 wait()
发布了67 篇原创文章 · 获赞 5 · 访问量 3188

猜你喜欢

转载自blog.csdn.net/weixin_41315492/article/details/103046462