Moosphan/Android-Daily-Interview

2019-07-23:Java 线程中notify 和 notifyAll有什么区别?

MoJieBlog opened this issue · 7 comments

2019-07-23:Java 线程中notify 和 notifyAll有什么区别?

notify 和 notifyAll是object的方法

当线程状态为等待、超时等待会调用notify 和 notifyAll方法通知线程更改状态,此时
当线程数量为1时,notify 和 notifyAll的效果一样,会唤醒一个线程,并获取锁
当线程数量大于1时,notify会唤醒一个线程,并获取锁,notifyAll会唤醒所有线程并根据算法选取其中一个线程获取锁,区别在于此时使用notify可能会出现死锁的情况

具体解释
Object的notify和notifyAll方法的区别

notify方法
1.方法调用之前由于也需要获取该对象的锁,所以使用的位置: synchronized方法中或者synchronized代码块中
2.通知那些可能等待该对象的对象锁的其他线程。如果有多个线程等待,则线程规划器任意挑选出其中一个wait()状态的线程来发出通知
3.wait()方法执行完毕之后,会立刻释放掉锁,如果没有再次使用notify,其他wait()的线程由于没有得到通知,会继续阻塞在wait()的状态,等待其他对象调用notify或者notifyAll来唤醒。
notifyAll方法
1.使用位置和notify一样
2.notifyAll唤醒所有处于wait的线程

当线程处于等待、超时等待的时候会调用notify和notifyAll通知线程更改状态,当只有一个线程的时候,这两个效果一样,会唤醒一个线程并获取锁。当线程数量大于1的时候notify会唤醒一个线程,nitifyAll会唤醒所有线程并选取其中一个线程获取锁。

notify和notifyall都是object的方法
当线程处于等待状态,超时等待的时候会调用notify和notufyAll通知线程更改状态,当只有一个线程的时候,两个方法的效果是一样的,当有多个线程的时候,notify会选择唤醒一个线程获取锁,notifyall会唤醒所有的线程,让它们从等待池都进入到该对象的锁池,然后其中一个线程竞争获取锁

notify、notifyAll和wait()是Java同步机制中的重要组成部分,与synchronized关键字结合使用。
在使用wait()方法使线程处于等待状态时,有两种情况:1.只有一个线程处于等待状态,这时候可以调用notify或者notifyAll方法使当先线程恢复原来的状态。2.有多个线程处于等待状态,调用notify方法会随机唤醒一个线程(线程优先级高的线程被唤醒的几率更大),调用notifyAll方法会唤醒所有的线程。

两者都是将等待线程中的线程调动至锁池,竞争锁对象,获得执行权,notify()是将等待池中的任意一个调至锁池,与锁池中的其他线程争夺锁对象,notifyAll()是将等待池中的全部线程调至锁池中。前者一般适用于仅有一个线程有效执行的情况,后者一般作用在全部。

区别
notify:只会唤醒等待该锁的其中一个线程。
notifyAll:唤醒等待该锁的所有线程。
既然notify会唤醒一个线程,并获取锁,notifyAll会唤醒所有线程并根据算法选取其中一个线程获取锁,那最终结果不都是只有一个线程获取锁吗?那JDK为什么还需要做出来这两个方法呢?这两种同步方法本质上会有什么区别?

这还要从对象内部锁的调度说起。

对象内部锁
其实,每个对象都拥有两个池,分别为锁池(EntrySet)和(WaitSet)等待池。

锁池:假如已经有线程A获取到了锁,这时候又有线程B需要获取这把锁(比如需要调用synchronized修饰的方法或者需要执行synchronized修饰的代码块),由于该锁已经被占用,所以线程B只能等待这把锁,这时候线程B将会进入这把锁的锁池。
等待池:假设线程A获取到锁之后,由于一些条件的不满足(例如生产者消费者模式中生产者获取到锁,然后判断队列为满),此时需要调用对象锁的wait方法,那么线程A将放弃这把锁,并进入这把锁的等待池。
如果有其他线程调用了锁的notify方法,则会根据一定的算法从等待池中选取一个线程,将此线程放入锁池。
如果有其他线程调用了锁的notifyAll方法,则会将等待池中所有线程全部放入锁池,并争抢锁。

锁池与等待池的区别:等待池中的线程不能获取锁,而是需要被唤醒进入锁池,才有获取到锁的机会。