多线程与高并发学习五

多线程与高并发学习五

如下源码阅读为JDK11版本

ReentrantLock源码

ReentrantLock的lock方法调用链路如下所示:
在这里插入图片描述
调用源码如下所示:
第一步:
在这里插入图片描述
第二步:
它会去找Sync的子类NonfairSync
在这里插入图片描述

第三步:
进入NonfairSync此处发现这个类没有aquire方法,然后就去它的父类找,然后父类的Sync也没有那个aquire方法,再去Sync的父类AbstractQueuedSychronizer找。
在这里插入图片描述
第四步找到该aquire方法
在这里插入图片描述
该aquire方法l里面的tryAquire()调用的是NonfairSync的 tryAcquire【此处的tryAcquire方法是模板方法模式,父类给定一个方法,子类调用的时候不断重写该方法实现子类的各种功能
第五步就进入下面的 nonfairTryAcquire
在这里插入图片描述
如上是获取当前线程,然后再调用AQS的getState()方法
在这里插入图片描述
AQS的核心就是如上的state变量,AQS结构如下所示:
这个state是根据不同的实现类取不同的意义,同时AQS里面也维护了如下图所示的队列(也是双向链表),其中node里面也装了Thread,其中node是通过cas的方式去改那个state改成功后,就说明那个线程持有这把锁,拿到锁之后由于现在是非公平的状态其他线程来抢不到锁则进入如下双向链表中等待。
在这里插入图片描述
在这里插入图片描述
如果此时的线程状态为0,则通过CAS的方式去更改state,假如改成功了就把当前这个线程设置成独占这个state的线程,这样就得到了锁,而且所得到的锁是互斥的。
如果当前的线程状态不是0并且当前的线程就是独占这个state的线程则更新当前线程的状态为c+aquires,就表示可重入了。

如上代码如果没有获取到锁的话则回到此方法,将其放在如下队列里面等待,里面有个addWaiter(添加一个排他形式的等待者)方法
在这里插入图片描述
进入addWaiter()方法:
在这里插入图片描述
此处看如上Params的注释:此处传入Node类型是独占类型就是独占锁,如果传入的是共享类型的就是共享锁。
接下来是一个死循环:
首先将tail记录在一个oldTail的变量里面,如果拿到的oldTail!=null那么就将新节点的前置节点设置为当前的oldTail,然后再通过CAS操作将我们的新的节点设置为tail,然后返回一个node到前一步的acquireQueued
进入如下代码在队列里面排队获得锁:
在这里插入图片描述
进入此方法就先通过node。predecessor()拿到前置节点,如果前置节点是头节点,并且通过tryAcquire尝试一下去获得这把锁,就相当于和前置节点竞争了,如果拿到了锁就把当前这个节点设置为头节点,否则的话则执行shouldParkAfterFailedAcquire让当前线程在这里阻塞,然后等着被前置节点叫醒。

下面我们来详细探究一下 addWaiter的 node.setPrevRelaxed(oldTail);这个方法
进入之后展示的是如下代码:
在这里插入图片描述
然后PREV是VarHandle类型的,这是JDK1.9之后才有的,其代表的是一个引用的指向【指向某个变量的引用】
在这里插入图片描述
为了搞清楚具体的作用,于是写了如下demo


import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

public class HelloVarHandle {

    int x = 8;
	//定义一个VarHandle
    private static VarHandle handle;

    static {
        try {
        //通过MethodHandles.lookup()返回lookup对象,然后再通过findVarHandle找HelloVarHandle.class,名字为x的类型是int的一个变量,然后再让handle指向它,然后通过handle也能找到那个x
            handle = MethodHandles.lookup().findVarHandle(HelloVarHandle.class, "x", int.class);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
//当有了handle的时候,我们就可以通过handle去操作x的值了
    public static void main(String[] args) {
       HelloVarHandle t = new HelloVarHandle();

        //plain read / write
        System.out.println((int)handle.get(t));
        handle.set(t,9);
        System.out.println(t.x);

        handle.compareAndSet(t, 9, 10);
        System.out.println(t.x);

        handle.getAndAdd(t, 10);
        System.out.println(t.x);

    }
}

最强大的一个地方我可以通过handle里面的compareAndSet方法去更改x的值,这是(原子性操作)线程安全的还可以getAndAdd这个也是原子性操作,使得普通的属性也可以进行原子性操作。
JDK9以前只能通过反射去完成这些操作,但是VarHandle比反射快很多,VarHandle可以理解为直接操作二进制码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值