关于为什么线程通信的方法wait()、notify()、notifyAll()被定义在Object类里面?而sleep为什么定义在Thread类里面? 您所在的位置:网站首页 p站为什么上不去了 关于为什么线程通信的方法wait()、notify()、notifyAll()被定义在Object类里面?而sleep为什么定义在Thread类里面?

关于为什么线程通信的方法wait()、notify()、notifyAll()被定义在Object类里面?而sleep为什么定义在Thread类里面?

2023-08-15 02:30| 来源: 网络整理| 查看: 265

导语

在面试中经常会被问到多线程的知识,而在这个知识范围内,我们也会被问到关于wait、notify等这些方法。一般基础点的都会问使用的方法,当然这只是初级的问题,那么稍微深点的问题,可能就像我们标题这样:为什么线程通信的方法wait()、notify()、notifyAll()被定义在Object类里面?而sleep定义在Thread类里面?

其实这样的问题更多的是体现在程序实际上面,意思就是说我们要从设计层面理解这样的问题,对于这样的问题,我们自己在日常的学习中,也需要自己多多的去思考,毕竟Java作为这样一个存活了几十年的语言,他其中的一些设计自然是有他的道理的,我们就不多说什么了,快点进入主题。

关于wait()、notify()的使用

可能又人会说了,你刚才不是说使用是最基础的嘛?不过只有当我们学会了使用后,然后才能从使用中去发现一些问题。因为使用就为了体现一些原理的,否则也只属于意淫的状态,说到这里我们先看段代码(关于标题中的notifyAll,本文章就不多做解释,因为它和notify类似,且都属于Object类),如下:

public class WaitDemo { public static Object object = new Object(); public static void main(String[] args) throws InterruptedException { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); Thread.sleep(1000); thread2.start(); } static class Thread1 extends Thread { @Override public void run() { synchronized (object) { System.out.println("线程" + Thread.currentThread().getName() + "开始执行!"); try { // System.out.println("线程" + Thread.currentThread().getName() + "调用了wait()"); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁!"); } } } static class Thread2 extends Thread { @Override public void run() { synchronized (object) { object.notify(); System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()"); } } } } wait()、notify()、notifyAll()被定义在Object类里面?

这个问题其实有点奇怪,我们就好比说wait方法不这么用,那怎么用。我们 先从代码中来看,首先在代码里面,我们创建了一个Object对象,然后我们synchronized锁定的便是object,我在之前的文章Java并发编程之synchronized底层实现原理中提到过,当进入synchronized时当前线程便会持有monitor,如果不能获取到这个monitor的话,wait方法是会抛异常的。当然从这个角度解析的话,是非常抽象的。

其实我们从代码中还可以看出这样的一个问题,就是我们在两个线程中所用的是一个对象,在Thread1中我们使用object对象来调用wait方法,然后在Thread2中使用object去调用notify方法来唤醒线程调用wait方法的线程,当前代码的执行顺序的结果是:

线程Thread-0开始执行 线程Thread-1调用了notify() 线程Thread-0获取到了锁

这从另一个角度也证明了,wait是用来释放锁的。从上面的整个代码逻辑中,可以看出wait、notify、notifyAll被设计在Object类中的原因是,JAVA提供的锁是对象级的而不是线程级的,每个对象都有个锁,而线程是可以获得这个对象的。因此线程需要等待某些锁,那么只要调用对象中的wait()方法便可以了。而wait()方法如果定义在Thread类中的话,那么线程正在等待的是哪个锁就不明确了。这也就是说wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中是因为锁是属于对象的原因。

sleep为什么定义在Thread类里面?

对于sleep为什么被定义在Thread中,我们只要从sleep方法的作用来看就知道了,sleep的作用是:让线程在预期的时间内执行,其他时候不要来占用CPU资源。从上面的话术中,便可以理解为sleep是属于线程级别的,它是为了让线程在限定的时间后去执行。而且sleep方法是不会去释放锁的(本段代码是为了展示线程sleep时,不释放synchronized的monitor,等sleep时间到了之后,正常结束之后才释放锁),我们可以写段代码来证明下:

public class SleepDontReleaseMonitor implements Runnable { public static void main(String[] args) { SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor(); new Thread(sleepDontReleaseMonitor).start(); new Thread(sleepDontReleaseMonitor).start(); } @Override public void run() { sync(); } private synchronized void sync() { System.out.println("线程" + Thread.currentThread().getName() + "获取到了monitor!"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "退出了同步代码块!"); } }

这段代码的执行结果是:

线程Thread-0获取到了monitor! 线程Thread-0退出了同步代码块! 线程Thread-1获取到了monitor! 线程Thread-1退出了同步代码块!

从执行结果可以看出只有当Thread-1退出同步代码块后,Thread-0才可以退出代码块,而Thread-0 又是先进入代码块的,由此可见sleep方法是不会去释放锁的,这便不满足在锁内使用的意义了。(修改后:从执行结果可以看出只有当Thread-0退出同步代码块后,Thread-1才可以继续获取锁,而Thread-0 又是先进入代码块的,由此可见sleep方法是不会去释放锁的monitor,这便满足在锁内使用的意义了。)

总结

在上面我们分别从几个方面对标题的疑问进行了简答的解析,就是从使用中的使用方式和最后的结果来进行分析,wait()、notify()、notifyAll()被定义在Object类里面和sleep为什么定义在Thread类里面的原因。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有