Java的锁池与对象池
前言
在Java程序的开发或者学习,常常遇到各种各样的锁,因为有内置锁的机制,每个对象都可以承担锁的功能
对象池与锁池
Java虚拟机会为每个对象维护两个“队列”(姑且称之为“队列”,尽管它不一定符合数据结构上队列的“先进先出”原则):一个叫Entry Set(入口集),另外一个叫Wait Set(等待集)。
锁池:
对于任意的对象objectX,objectX的Entry Set用于存储等待获取objectX这个锁的所有线程,也就是传说中的锁池
对象池:
objectX的Wait Set用于存储执行了objectX.wait()/wait(long)的线程,也就是等待池。
具体解释
锁池
- 假设objectX是任意一个对象,假设有线程A、B、C同时申请objectX这个对象锁
- 那么由于任意一个时刻只有一个线程能够获得(占用/持有)这个锁,因此除了胜出(即获得了锁)的线程(这里假设是B)外,其他线程(这里就是A和C)都会被暂停(线程的生命周期状态会被调整为BLOCKED),这些因申请锁而落选的线程就会被存入objectX对应的锁池之中。
- 当objectX被其持有线程(这里就是B)释放时,锁池中的一个任意(注意是“任意”,而不一定是锁池中等待时间最长或者最短的)线程会被唤醒(即线程的生命周期状态变更为RUNNABLE)
- 这个被唤醒的线程会与其他活跃线程(即不处于锁池之中,且线程的生命周期状态为RUNNABLE的线程)再次抢占objectX
- 被唤醒的线程如果成功申请到objectX,那么该线程就从锁池中移除。否则,被唤醒的线程仍然会停留在锁池中,并再次被暂停,以等待下次申请锁的机会
对象池
- 如果有个线程执行了objectX.wait(),那么该线程就会被暂停(线程的生命周期状态会被调整为Waiting),并且会释放掉objectX锁,然后被存入objectX的等待池之中
- 此时,该线程就被称为objectX的等待线程
- 当其他线程执行了objectX.notify()/notifyAll()时,等待池中的一个(或者多个,取决于被调用的是notify还是notifyAll方法)任意(注意是“任意”,而不一定是等待池中等待时间最长或者最短的)等待线程会被唤醒,这些被唤醒的线程会被放到锁池中,会与锁池中已经存在的线程以及其他(可能的)活跃线程共同参与抢夺objectX。
- 至于代码中到底是使用notify还是notifyAll方法,这个要根据实际情况来分析。
- 如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池。
PS:等待池中的线程被notify()或者notifyAll()方法唤醒进入到锁池,最后竞争到了锁并且进入了Runnable状态的话,会从wait现场恢复,执行wait()方法之后的代码