为什么 wait, notify 和 notifyAll 这些方法不在 thread 类里面?

Java提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。简单的说,由于wait,notify,notifyAll都是锁级别的操作,所以把他们定义在object类中因为锁属于对象。

如果wait, notify 和 notifyAll 这些方法在 thread 类里面会有什么问题?

 wait方法仍然可以使当前线程挂起,但问题是挂起后怎么被其他线程唤醒(唤醒线程时需要知道要唤醒那个线程,参考:LockSupport.unpark(thread)),可以通过共享变量暴露线程但是却存在安全隐患。

notify和notifyall同样需要知道需要唤醒那些线程。

这样会使线程间的通信复杂化而且存在安全隐患

java的Object里wait()实现原理

 在进行wait()之前,就代表着需要争夺锁(内嵌锁),而Synchronized代码块通过javap生成的字节码中包含monitorenter和monitorexit两个指令。在进入锁的时候会执行monitorenter,执行monitorenter指令可以获取对象的monitor。同时在执行Object.wait()的时候也必须持有monitor对象。

在多核环境下,多个线程有可能同时执行monitorenter指令,并获取lock对象关联的monitor,但只有一个线程可以和monitor建立关联,这个线程执行到wait方法时,wait方法会将当前线程放入wait set,使其进行等待直到被唤醒,并放弃lock对象上的所有同步声明,意味着该线程释放了锁,其他线程可以重新执行加锁操作,notify方法会选择wait set中任意一个线程进行唤醒,notifyAll方法会唤醒monitor的wait set中所有线程。执行完notify方法并不会立马唤醒等待线程。那么wait具体是怎么实现的呢?

首先在HotSpot虚拟机中,monitor采用ObjectMonitor实现,每个线程都具有两个队列,分别为free和used,用来存放ObjectMonitor。如果当前free列表为空,线程将向全局global list请求分配ObjectMonitor。 

ObjectMonitor对象中有两个队列,都用来保存ObjectWaiter对象,分别是_WaitSet 和 _EntrySet。_owner用来指向获得ObjectMonitor对象的线程 。

ObjectWaiter对象是双向链表结构,保存了_thread(当前线程)以及当前的状态TState等数据, 每个等待锁的线程都会被封装成ObjectWaiter对象。 

_WaitSet :处于wait状态的线程,会被加入到wait set;

_EntrySett:处于等待锁block状态的线程,会被加入到entry set;

 wait方法实现:

lock.wait()方法最终通过ObjectMonitor的void wait(jlong millis, bool interruptable, TRAPS)实现:

  1. 将当前线程封装成ObjectWaiter对象node
  2. 通过ObjectMonitor::AddWaiter方法将node添加到_WaitSet列表中
  3. 通过ObjectMonitor::exit方法释放当前的ObjectMonitor对象,这样其它竞争线程就可以获取该ObjectMonitor对象
  4. 最终底层的park方法会挂起线程

ObjectSynchorizer::wait方法通过Object对象找到ObjectMonitor对象调用方法void ObjectMonitor::wait(),通过ObjectMonitor::AddWaiter调用把新建里的ObjectWaiter对象,放入到_WaitSet队列的末尾然后,在ObjectMonitor::exit释放锁,接着thread_ParkEvent->park,也就是进行wait。

 转载:https://blog.csdn.net/ZytheMoon/article/details/89461016

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页