(11)重量级锁(Monitor)的加锁和解锁流程?解锁是有顺序的吗?

重量级锁(Monitor)加锁流程和解锁流程

重量级锁 MonitorObject 对象有 4 个属性,分别是:

  • _owner:当前锁的持有线程

  • _cxq:竞争栈

  • _entryList:一个队列

  • _waitSet:

加锁流程:

        当线程 t1、t2、t3 一起获取一个重量级锁时,获取的时间顺序分别是 t1、t2 和 t3。

1、因为是线程 t1 首先到达,所以 t1 会获取成功, MonitorObject 的 _owner 会从 nullptr 变成 t1,线程 t1 的 markword 对象存储 MonitorObject 的地址引用并将最后两位标记为 10,表示重量级锁。此时线程 t2 和 t3 肯定获取锁失败。

2、线程 t2,t3 开获取失败后悔开始进行自旋操作【jdk1.6 以后固定自旋就弃用了】,首先预自旋 11 次,获取锁失败之后会自适应自旋,首先自旋 5000 次。在自旋期间若线程 t1 释放锁了,此时线程 t2 和 t3 会一起去抢占锁,若没有释放就会进入 _cxq 竞争栈中。这段时间还是抢占式的。

3、若第二步获取锁失败了,就会进入一个叫做 enterI 的方法,尝试获取 _owner ,失败之后会陷入自旋,较上次自旋次数少 200 次,若自旋期间获取成功就成功拿到锁了,这段时间还是抢占式的。

4、若第 3 步中还是获取失败了那么线程就会在 _cxq 中陷入阻塞状态了(park),直到 _owner 被释放才会被唤醒。从这里开始就是非抢占式的了。靠后竞争锁的线程会优先获取到锁。

解锁流程:

        当线程 t1 释放锁之后,就会将 _owner 设置成 nullptr。此时会根据 _cxq 和 _entryList 的状态做出不同的操作。

1、当_cxq 和 _entryList 都为空时直接返回,释放成功。

2、当 _cxq 不为空时,就会将 _cxq 中所有的节点移动到 _entryList 中,_cxq 按照后进先出的原则,之后进入 _cxq 的会先进入 _entryList。

3、当 _entryList 不为空时,使用 unpark 方法从队列头结点开始唤醒,然后返回。

所以说,根据上面的加锁流程,当 t1 释放锁之后,进入 _cxq 的顺序是先 t2 后 t3,所以离开 _cxq 进入 _entryList 的顺序是先 t3 后 t2。故在 t2 和 t3 中,t3 会先获得锁。

解锁是有序的验证

查看下列代码

   
 private static Object obj = new Object();
​
    public static void main(String[] args) throws InterruptedException {
​
        new Thread(() -> {
            System.out.println("t1 获取锁");
            synchronized (obj) {
                try {
                    System.in.read();
                    System.out.println("t1 释放");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
​
        }).start();
​
        Thread.sleep(100);
        new Thread(() ->{
            synchronized (obj){
                System.out.println("t2 获取");
            }
        }).start();
​
        Thread.sleep(100);
        new Thread(() ->{
            synchronized (obj){
                System.out.println("t3 获取");
            }
        }).start();
​
        Thread.sleep(100);
        new Thread(() ->{
            synchronized (obj){
                System.out.println("t4 获取");
            }
        }).start();
​
    }

运行结果如下(运行多次结果都是一样的):从结果可以看出,当线程 t1 释放锁后,越靠后竞争锁的线程或优先抢占到锁。这就是上面加锁流程中第 4 步的体现。

t1 获取锁
​
t1 释放
t4 获取
t3 获取
t2 获取
​
Process finished with exit code 0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MonitorJava中的同步机制,用于实现线程之间的互斥和协作。在Java中,每个对象都有一个与之关联的监视器(monitor)。当一个线程需要访问某个对象时,它必须先获得该对象的监视器,进入对象的监视器后,线程才能执行对象的同步方法或同步块。 在Java中,监视器的实现是通过内置(也称为互斥)来实现的。内置是一种可重入的互斥,它可以保证同一时间只有一个线程能够获得,其他线程则被阻塞。 一个线程要获得一个对象的监视器,必须执行以下操作: 1.尝试获取对象的,如果对象的未被占用,则获取,并将的持有者设置为当前线程。 2.如果对象的已经被占用,则当前线程将被阻塞,直到被释放。 3.当线程执行完对象的同步方法或同步块时,它会自动释放对象的。 在Java中,每个对象都有一个监视器和一个等待队列。当一个线程调用对象的wait()方法时,它就会进入等待队列,并释放对象的。当另一个线程调用对象的notify()方法时,它会从等待队列中选取一个线程,将其唤醒,并将对象的交给该线程。 重量级指的是在获取时,如果已经被其他线程占用,则当前线程会进入阻塞状态,直到被释放。这种的开销比较大,因为它需要频繁地进行上下文切换和线程阻塞。 在Java中,当一个线程尝试获取一个对象的时,如果已经被其他线程占用,则该线程会进入阻塞状态,并将自己加入到对象的等待队列中。当被释放时,等待队列中的线程会被唤醒,并竞争的所有权。这种的实现方式称为重量级重量级解锁过程比较简单,只需要将的状态设置为未占用,并唤醒等待队列中的一个线程即可。如果等待队列中没有任何线程,则解锁过程就结束了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值