欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-04 20:50:06
...

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();
    }
}

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

wait,notify,notifyall特点、性质

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

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

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

Java多线程之 - wait,notify,notifyall作用、用法
生产者 把生产出来的数据放入 中间的阻塞队列 消费者再通过阻塞队列去取 这样一来就有个缓冲区这样就是把他们的能力进行平衡 ,当生产者生产数据过多时阻塞消费者消化,等到差不多是再提醒生产者继续生产 。
Java多线程之 - wait,notify,notifyall作用、用法

消费者 生产者模式代码 实现 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();
        }
    }
}

Java多线程之 - wait,notify,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()