zoukankan      html  css  js  c++  java
  • 线程的等待与唤醒

    概述

    1.wait、notify介绍,与锁的关系;

    2.wait、notify、notifyAll的使用;

    3.生产者消费者通过wait、notify来实现

    wait、notify介绍,与锁的关系

    1.wait、notify、notifyAll不属于Thread类,而是属于object类,也就是说每个对象都有这几种方法。那为什么要放到object类? 因为每个对象都有锁,而wait、notify、notifyAll是基于对象锁的。

    wait会使当前线程进入等待状态,直到其他线程调用此对象的notify或notifyAll方法,或被其他线程中断。调用wait的线程必须持有对象锁。

    notify 唤醒在此对象监视器上的单个线程,如果有多个线程则随机唤醒一个。直到当前线程释放锁,才能执行被唤醒的线程。notify也只能被持有对象锁的线程调用。

    notifyAll也是一样,不同的是notifyAll会唤醒此对象监视器上的所有线程。

    只能被持有对象锁的线程调用意味着wait/notify/notifyAll必须和synchronized方法/代码块中使用,而synchronized代码块内必须要有锁。

    可以试一下,在一个非synchronized代码块中调用wait/notify会出现什么问题?

    以下两个例子分别为在非synchronized代码块中调用和在synchronized代码块中调用但是未持有对象锁

    public class WaitNotify {
    
        public static void main(String[] args) throws InterruptedException {
    
            Object obj = new Object();
            obj.wait();
            obj.notifyAll();
        }
    }
    
    报错:
    Exception in thread "main" java.lang.IllegalMonitorStateException
    public class WaitNotify {
    
        public static void main(String[] args) throws InterruptedException {
    
            Object obj = new Object();
            Object lock = new Object();
            synchronized (lock) {
                obj.wait();
                obj.notifyAll();
            }
        }
    }
    
    报错:
    Exception in thread "main" java.lang.IllegalMonitorStateException

    为什么必须获取该对象的锁?

    因为在没有锁的情况下,wait和notify会出现竞态,举个常用的生产者消费者的例子

    假设有如下情景:

    1.生产者生成->2.生成队列满了->3.生产者进入等待状态

    a.消费者消费一个->b.生产者队列刷新->c.唤醒生产者线程

    在多线程的情况下,可能会出现这样的顺序:1->a->b->c->3,这样无对象锁会造成的问题就是生产者还没进入等待状态,消费者就已经notify了,这样生产者会一直处在wait状态;

    所有必须通过对象锁来实现,即队列对象的锁。来保证同步

    wait、notify、notifyAll的使用

    直接上代码了,在注释里面说明

    public class RunnableTest implements Runnable {
        private Integer ticket;
        public RunnableTest(int ticket){
    
            this.ticket=ticket;
        }
        @Override
        public void run() {
    
            synchronized (ticket){ //对ticket对象进行synchronized
                try {
                    System.out.println(Thread.currentThread().getName()+" wait...");
                    ticket.wait();
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
            for(int i=0;i<10;i++){
    
                if(this.ticket>0){
    
                    System.out.println(Thread.currentThread().getName()+" sell "+this.ticket--);
    
                }
            }
        }
    
        public static void main(String[] args){
    
            RunnableTest runnableTest=new RunnableTest(5);
            Thread test1=new Thread(runnableTest);
            Thread test2=new Thread(runnableTest);
            Thread test3=new Thread(runnableTest);
            test1.start();
    
            test2.start();
            System.out.println("after test2");
    
            test3.start();
            //到这里,三个线程会进入等待状态
            try {
                Thread.sleep(1000); //这时主线程等待1s,主要担心三个线程可能有某个线程还未wait就调用了notify了,这样线程会一直等待。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            synchronized (runnableTest.ticket){
    
                    runnableTest.ticket.notify();  //唤醒线程,这时输出结果还有两个线程处于等待状态, 如果使用notifyAll就不会。
            }
    
        }
    }

    生产者消费者通过wait、notify来实现

    //生产者消费者类
    public class ProduceConsumerWaitNotify {
    
    
        public static void main(String[] args){
    
            QueueClass resource = new QueueClass();
            //生产者线程
            Producer p = new Producer(resource);
            //多个消费者
            Consumer c1 = new Consumer(resource);
            Consumer c2 = new Consumer(resource);
            Consumer c3 = new Consumer(resource);
    
            p.start();
            c1.start();
            c2.start();
            c3.start();
        }
    }
    
    class Producer extends Thread{
    
        QueueClass queueClass;
        public Producer(QueueClass queueClass){
    
            this.queueClass=queueClass;
        }
    
        @Override
        public void run() {
    
            while(true){
    
                queueClass.add();
                try {
                    Thread.sleep((long)(1000*Math.random()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Consumer extends Thread{
    
        QueueClass queueClass;
        public Consumer(QueueClass queueClass){
    
            this.queueClass=queueClass;
        }
    
        @Override
        public void run() {
    
            while(true){
    
                try {
                    Thread.sleep((long) (1000 * Math.random()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                queueClass.remove();
    
            }
        }
    }
    //队列实现,通过wait,notifyAll来实现
    public class QueueClass {
    
        private int size=10;
        private int count=0;
    
        public synchronized void add(){
    
            if(count<size){
    
                count++;
                System.out.println(Thread.currentThread().getName()+" produce,now is "+count);
                notifyAll(); //唤醒在QueueClass对象锁上的等待线程
            }else{
    
                try {
                    wait(); //生成队列满了,持有当前对象锁的线程进入等待
                    System.out.println(Thread.currentThread().getName()+" producer waiting...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public synchronized void remove(){
    
            if(count>0){
    
                count--;
                System.out.println(Thread.currentThread().getName()+" consumer,now is "+count);
                notifyAll();
            }else{
    
                try {
                    wait();
                    System.out.println(Thread.currentThread().getName()+" consumer waiting...");
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
  • 相关阅读:
    mybatis整合redis二级缓存
    python字符串非空判断
    mybatis源码分析之05一级缓存
    mybatis框架之动态代理
    Redis事件通知示例
    springboot2集成redis5报错:io.lettuce.core.RedisException: io.lettuce.core.RedisConnectionException: DENIED Redis is running in protected
    Centos7关闭防火墙
    mybatis源码分析之04Mapper接口的动态代理
    mybatis源码分析之03SqlSession的创建
    mybatis源码分析之02配置文件解析
  • 原文地址:https://www.cnblogs.com/dpains/p/7490252.html
Copyright © 2011-2022 走看看