• 【Java并发】wait、notify和notifyAll()及IllegalmoitorStateException异常


    这三个都不是Thread里的方法,而是Object里的方法。即每一个对象都有这三个方法。

    wait()

    使得当前正持有该对象的锁的线程等待(即暂停),并释放锁,以便其它线程能够获取该对象的锁。

    notify()

    唤醒一个正在等待该对象锁的线程(即处于wait状态的线程),具体哪一个不确定。

    notifyAll()

    唤醒所有正在等待该对象锁的线程(即处于wait状态的线程)。

    wait(time)

    可以指定等待时间,如果指定时间内没有notify或者notifyAll唤醒它,则时间到了后自动唤醒,如果时间还没到但是notify或者notifyAll唤醒它,则会提前唤醒。

    wait和sleep的区别

    wait会释放对象锁,而sleep不会;

    wait可以通过notify或者notifyAll唤醒,也可以指定时间,到时间后自动唤醒,而sleep只能指定时间。


    测试:

        public class Test3 {
        public static void main(String[] args) throws InterruptedException {
            Integer a = new Integer(1);
            Thread t1 = new Thread(new ThreadA(a),"thread-A");
            Thread t2 = new Thread(new ThreadB(a),"thread-B");
            t1.start();
            Thread.sleep(1000);
            t2.start();
            Thread.sleep(10);
            System.out.println(a);
        }
        }
        class ThreadA implements Runnable{
        private  Integer a ;
        public ThreadA(Integer a){
            this.a = a;
        }
        @Override
        public void run() {
            synchronized (this.a){
               // a++;
                try {
                    a.wait(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+" 调用a.wait(),释放了 a 锁,以便其他线程可以获取 a 的锁");
        }
        }
    
        class ThreadB implements Runnable{
        private Integer a;
        public ThreadB(Integer a){
            this.a = a;
        }
        @Override
        public void run() {
            synchronized (this.a){
                a++;
                a.notifyAll();
            }
            System.out.println(Thread.currentThread().getName()+" 调用a.notifyAll(),唤醒其他正在等待 a 的锁的线程");
        }
    
    
        }

    这样会抛出异常:

    原因:

    因为Integer在执行++操作时会重新生成一个对象,所以导致synchronized里的a在执行a++之后不是同一个对象了(可以将a++看成一个新对象),而再调用a.wait()或者a.notifyAll()就会抛异常,因为要求wait()或者notify()或者notifyAll()必须在synchronized内操作。

    根本原因是因为Integer中的value是final类型的,不能被修改,所以在执行++操作时,生成的是另一个对象。

    不单单是Integer,其他包装类也会这样。

    解决办法:

    将Integer换成AtomicInteger。

    或者使用自定义类,这样就会引用到同一个对象,如下:

        public class Test3 {
        public static void main(String[] args) throws InterruptedException {
            Person person = new Person();
            Thread t1 = new Thread(new ThreadA(person),"thread-A");
            Thread t2 = new Thread(new ThreadB(person),"thread-B");
            t1.start();
            Thread.sleep(1000);
            t2.start();
            Thread.sleep(10);
            System.out.println(person.getAge());
        }
        }
        class ThreadA implements Runnable{
        /**
         * 这里如果传入的是基本数据类型或者其包装类,在对其赋值时调用wait()或者notify()或者notifyAll时
         * 会抛出java.lang.IllegalMonitorStateException异常
         */
        private  Person a ;
        public ThreadA(Person a){
            this.a = a;
            System.out.println(a==a);
        }
        @Override
        public void run() {
            synchronized (this.a){
                a.setAge(a.getAge()+1);
                try {
                    a.wait(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+" 调用a.wait(),释放了 a 锁,以便其他线程可以获取 a 的锁");
        }
        }
    
        class ThreadB implements Runnable{
        private Person a;
        public ThreadB(Person a){
            this.a = a;
        }
        @Override
        public void run() {
            synchronized (this.a){
                a.setAge(a.getAge()+1);
                System.out.println(this.a==a);
                a.notifyAll();
            }
            System.out.println(Thread.currentThread().getName()+" 调用a.notifyAll(),唤醒其他正在等待 a 的锁的线程");
        }
    
    
        }
        class Person{
        private int age;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
    
        }

  • 相关阅读:
    基金定投是什么?定投的特点?
    Linux环境下MySQL 5.6安装与配置----亲测有效----纯离线安装
    OI生涯回忆录
    NOI2020游记
    Redis操作
    Redis概述
    Memcached
    动态规划——最长回文字符串
    两数之和&无重复字符最长字符串
    黑盒测试常见方法
  • 原文地址:https://www.cnblogs.com/cnsec/p/13286711.html
Copyright © 2020-2023  润新知