并发编程之AbstractQueuedSynchronizer

首先简单介绍下AbstractQueuedSynchronizer是并发编程的同步器工具框架

特点:

1:阻塞式和相关同步器工具框架

2:AQS用一个state变量表示锁状态,子类去维护这个状态

3:独占锁只允许一个线程访问资源

4:共享模式可以允许多个线程共享资源

5:内部维护了一个等待队列,

6:条件变量来实现等待,唤醒机制,支持多个变量

我们如果需要实现自己的锁,可以去继承这个类,去实现自定义锁的逻辑

下面我们以ReentrantLock为例简单介绍一下加锁逻辑:

它持有了一个Sync静态类属性,继承了AbstractQueuedSynchronizer

AbstractQueuedSynchronizer主要属性介绍:

等待队列的头结点
private transient volatile Node head;

等待队列的尾部结点 
private transient volatile Node tail;
同步器状态,默认为0表示未获取锁,1表示获取锁,支持重入,同一个线程加锁,state会+1,释放锁-1
private volatile int state;
再其父类还有一个属性,exclusiveOwnerThread 表示同步器持有锁的线程
 

我们通过构造器创建实例,可以指定布尔值,表示这个锁是否公平锁,还是非公平锁,默认是非公平锁

调用lock方法,默认会调用到非公平锁内部类的lock方法

 

 场景1: 此时T1线程进行加锁,通过CAS设置同步器的状态status =1 ,设置成功表示加锁成功,并将持有锁的线程更新为当前线程,加锁过程完毕.

场景2: T1线程未释放锁,T2线程进行加锁

T2线程通过CAS更改state状态值,肯定会失败,此时进入acquire方法

 调用tryAcquire()方法尝试进行加锁

获取当前线程T2,通过getState获取同步器状态,此时T1未释放锁,c==0不成立

并且当前持有锁的线程是T1,else if代码块逻辑也不会执行,返回false

 tryAcquire方法的返回false,代码取反,所以会走addWait方法

private Node addWaiter(Node mode) {
     //构造Node节点 thread=t2,pre=null ,next=null waitstatus=0
    Node node = new Node(Thread.currentThread(), mode);
    //将同步器的tail指针复制给pre ,此时tail指针是null
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //进入enq方法
    enq(node);
    return node;
}

//入队操作
private Node enq(final Node node) {
    for (;;) {
        //同步器尾部节点赋值给t=null
        Node t = tail; 
        if (t == null) { // Must initialize
            //新创建一个节点Node(thread=null pre=null next=null waitStatus=0)
            //使用CAS设置Node节点为头部节点
            if (compareAndSetHead(new Node()))
                //设置尾部节点也为Node
                tail = head;
        } else {
            //第二次循环,将线程T2的pre指针指向新创建Node节点
            node.prev = t;
            //设置尾部节点为T2线程
            if (compareAndSetTail(t, node)) {
                //设置Node节点next指针Node(thread=null pre=null next=指向T2 waitStatus=0)
                t.next = node;
                return t;
            }
        }
    }
}

第一次循环

 第二次循环示意图,结束循环后将T2的Node节点返回

 

调用acquireQueued

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            //T2线程的前一个节点,并再次尝试加锁,此时加锁任然失败
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //获取锁失败之后,将
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
//第一次循环返回false,并更新waitStatus=-1,第二次循环进入返回true,并将T2线程park阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
  //ws==0    
int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        
        return true;
    if (ws > 0) {
        
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
              //将Node节点waitStatus设置为-1,表示需要唤醒后面的节点
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

waitStatus 表示队列中的节点是否需要唤醒别人

0 表示没有任何意义

-1 表示在队列中等待,需要唤醒后继节点

-2 表示条件队列状态 conditionObject

释放锁代码很简单就不再这里赘述了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值