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

导语

在面试中经常会被问到多线程的知识,而在这个知识范围内,我们也会被问到关于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方法是不会去释放锁的,我们可以写段代码来证明下:

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 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-1获取到了monitor! 
线程Thread-1退出了同步代码块! 
线程Thread-0退出了同步代码块!

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

 

总结

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

 

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/19404.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论