五.Thread和Object类中的重要方法详解

本文详细解析了Java中Thread和Object类的重要方法,包括wait、notify和notifyAll的使用、原理、特点及注意事项。通过实例展示了如何在生产者消费者模式和交替打印奇偶数场景中应用这些方法,并对比了它们与sleep、join的区别。文章还探讨了面试中常见的线程通信问题。
摘要由CSDN通过智能技术生成

1.方法概览

在这里插入图片描述

2.wait,notify,notifyAll

作用、用法

注意wait的使用一定要在一个循环中进行,根据条件判断是否继续执行

伪唤醒

/*A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:
线程也可以在不被通知、中断或超时的情况下被唤醒,这就是所谓的伪唤醒(原因可能是OS内部实现/报错/其它线程在被唤醒线程执行前修改了条件[例如生产者消费者模式中生产被唤醒后唤醒一个消费者,但是生产者还没有生产出值,消费者就去消费了])。虽然这种情况在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防止这种情况发生,如果条件不满足,则继续等待。换句话说,等待应该总是在循环中发生,就像下面这个循环
*/
           synchronized (obj) {
   
               while (<condition does not hold>)
                   obj.wait(timeout);
               ... // Perform action appropriate to condition
           }

有时需要线程暂时等待,这时候就需要wait方法.当需要或条件达成的时候再唤醒,这时候就需要notify和notifyAll,

而这三个方法的前提条件就是在synchronized(ObjectInstance)代码块/方法中获取ObjectInstance的monitor lock且需要是ObjectInstance进行调用,否则就会出现异常

//IllegalMonitorStateException – if the current thread is not the owner of the object's //monitor.
    java.lang.Object#wait()

当通过ObjectInstance调用wait方法时,调用线程会被阻塞,同时释放锁资源.

直到以下四种情况之一发生,才会被唤醒

1.另一个线程调用ObjectInstance的notify()[只唤醒一个阻塞在ObjectInstance上的线程]且恰好唤醒了这个线程.

2.另一个线程调用ObjectInstance的notifyAll(唤醒所有阻塞在ObjectInstance上的线程)

3.过了wait(long timeout)规定的超时时间,如果传入0就是永久等待(默认不带参为0);

4.线程自身调用了interrupt()

notify的调用会唤醒在ObjectInstance的monitor上等待的单个线程,如果有多个线程在这个monitor等待,则选择其中的一个唤醒,这个选择是任意的,由用来判断的实现(调度器)决定.

notifyAll的调用区别于notify的就是它会唤醒所有在ObjectInstance的monitor上等待的线程.

注意,唤醒后,在当前线程放弃对ObjectInstance的monitor锁之前(也就是离开synchronized代码块或退出synchronized方法),被唤醒的线程将无法继续工作(因为当前线程持有的锁还没有释放,被唤醒线程还没有持有锁)

当ObjectInstance的monitor锁已经没有线程持有后,被唤醒的线程将继续之前的竞争monitor lock的流程,不享有任何的特权或劣势.

代码演示(四种情况)

Wait和notify
    public static void main(String[] args) throws InterruptedException {
   
        new Thread(()->{
   
            synchronized (Thread.class){
   
                System.out.println("first_wait_释放锁");
                try {
   
                    Thread.class.wait();
                    //唤醒后无法立即执行,因为second还没有退出synchronized
                    System.out.println("first>>>continue");
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            }
        }).start();
        //确保first一定先执行
        Thread.sleep(100);
       new Thread(()->{
   
           synchronized (Thread.class){
   
               System.out.println("second获得锁");
               Thread.class.notify();
               try {
   
                   System.out.println("second_notify已调用");
                   Thread.sleep(5000);
                   System.out.println("second结束睡眠");
               } catch (InterruptedException e) {
   
                   e.printStackTrace();
               }
               System.out.println("second>>>退出synchronize");
           }
       }).start();
    }

结果

first_wait_释放锁
second获得锁
second_notify已调用
second结束睡眠
second>>>退出synchronize
first>>>continue
如果触发中断,将释放持有的monitor
    public static void main(String[] args) {
   
        synchronized (Thread.class) {
   
            try {
   
                Thread.class.wait();
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
        }
    }
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // class java/lang/Thread
       2: dup
       3: astore_1
       4: monitorenter
       5: ldc           #2                  // class java/lang/Thread
       7: invokevirtual #3                  // Method java/lang/Object.notify:()V
      10: ldc           #2                  // class java/lang/Thread
      12: invokevirtual #4                  // Method java/lang/Object.wait:()V
      15: goto          23
     //触发了interruptedtException,将栈顶引用存储保存在本地变量slot2
      18: astore_2
     //从本地变量slot2加载引用变量到栈顶
      19: aload_2
     //根据栈顶引用调用Method
      20: invokevirtual #6                  // Method java/lang/InterruptedException.printStackTrace:()V
      //加载astore_1存入的引用到栈顶
      23: aload_1
      //根据栈顶引用变量指向对象释放monitor
      24: monitorexit
      25: goto          33
      28: astore_3
      29: aload_1
      30: monitorexit
      31: aload_3
      32: athrow
      33: return
    Exception table:
       from    to  target type
          10    15    18   Class java/lang/InterruptedException
           5    25    28   any
          28    31    28   any

NotifyAll
    public static void main(String[] args) throws InterruptedException {
   
        new Thread(WaitAndNotifyAll::run).start();
        new Thread(WaitAndNotifyAll::run).start();
        //确保notifyAll一定最后执行.
        //因为start()之后代表线程变为Runnable状态,具体什么时候运行
        //由OS的调度器决定(CPU资源充足时一般会尽快执行)
        Thread.sleep(100);
        new Thread(()->{
   
            synchronized (Thread.class){
   
                System.err.println("开始唤醒");
                Thread.class.notifyAll();
                System.err.println("唤醒结束,退出synchronized");
            }
        }).start();
    }
    public static void run(){
   
        synchronized (Thread.class){
   
            try {
   
                System.out.println(Thread.currentThread().getName()+"wait");
                Thread.class.wait();
                System.out.println(Thread.currentThread().getName()+"been notified");
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
        }
    }

结果

Thread-0wait
Thread-1wait
开始唤醒
唤醒结束,退出synchronized
Thread-1been notified
Thread-0been notified
wait只释放当前对象锁,notify/notifyAll只唤醒被当前对象锁住的线程
    public static void main(String[] args) {
   
        new Thread(WaitMulti::run).start();
        new Thread(WaitMulti::run1).start();
    }
    public static void run() {
   
        try {
   
            synchronized (WaitMulti.class) {
   
                System.out.println("线程A" + "进入waitMulti,执行wait,释放WaitMulti锁");
                WaitMulti.class.wait();
                synchronized (Thread.class) {
   
                    System.out.println("线程A进入Thread,");
                    System.out.println("线程A退出Thread.class");
                }
                System.out.println("线程A退出WaitMulti.class");
            }
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }
    public static void run1() {
   
        try {
   
            Thread.sleep(100);
            synchronized (WaitMulti.class) {
   
                System.out.println("线程B进入WaitMulti,获得锁,唤醒线程A_WaitMulti阻塞,");
                WaitMulti.class.notify();
                System.out.println("线程B依旧持有waitMulti锁");
                synchronized (Thread.class) {
   
                    System.out.println("线程B进入thread,执行wait,释放thread锁");
                    System.out.println("线程B依旧持有WaitMulti锁");
                    Thread.class.wait();
                    System.out.println("线程B退出了synchronized_Thread.class");
                }
                System.out.println("线程B退出了synchronized_WaitMulti.class");
            }
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }

结果
在这里插入图片描述

可以看到依旧在waitMulti依旧在等待

正确释放锁

    public static void main(String[] args) {
   
        new Thread(WaitMulti::run).start();
        new Thread(WaitMulti::run1).start();
    }
    public static void run() {
   
        try {
   
            synchronized (WaitMulti.class) {
   
                System.out.println("线程A" + "进入waitMulti,获得进入waitMulti锁,执行wait,释放WaitMulti锁");
                WaitMulti.class.wait();
                synchronized (Thread.class) {
   
                    System.out.println("线程A获得了WaitMulti锁,进入Thread,获得了thread锁");
                    System.out.println("线程A退出Thread,释放了Thread锁");
                }
                System.out.println("线程A退出WaitMulti,释放了WaitMulti锁");
            }
        } catch (InterruptedException e) {
   
            e.printStackTrace
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值