Java锁及AbstractQueuedSynchronizer源码分析

Posted dupang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java锁及AbstractQueuedSynchronizer源码分析相关的知识,希望对你有一定的参考价值。

- lock 

Lock实现提供了比使用synchronized方法和synchronized语句块扩展性更好的锁操作,他们允许更灵活地构建,可以有相当不同的属性,并且可以支持多个相关的Condition对象
锁是一个控制被多个线程共享的资源访问的工具,一般来说,锁对共享资源提供排它地访问,
一个时间内只能一个线程可以获取锁并且要求获取锁的线程是所有对共享资源的访问线种中的第一个。
然而,一些锁可以允许对共享的资源并发访问,比如ReadWriteLock的读锁

关于锁的几个概念

锁重入
就是一个线程获取锁之后,这个线程还可以再次获取相同锁。
公平锁
获取锁的过程采用类似排队的机制,排在前面的线程先获取锁。
非公平锁
获取锁的过程,不排队,只要没有线程持有锁,就可以获取锁。


ReentrantLock是可重入锁的一个实现。
AbstractQueuedSynchronizer是一个抽象类。它提供了很多关于锁的基本操作。

ReentrantLock类里有一个sync的字段。它继承于AbstractQueuedSynchronizer。

先看获取锁的过程
public void lock() {
        sync.lock();
    }

它直接调用的sync对象的lock()方法。

Sync类提供了公平锁和非公平锁的公共操作。它的lock方法是一个抽象方法。具体实现在公平锁
FairSync和非公平锁实现NonfairSync中实现。首先先看公平锁版本的实现


final void lock() {
       acquire(1);
}

acquire(1)这方法是在在AbstractQueuedSynchronizer中实现

 

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

它大致做了如下几个事情。

1 tryAcquire() 就是试图获取锁,如果成功就结束

2 acquireQueued 如果试图获取失败,就加入队列中等待

3 addWaiter 新建表示当前线程的一个节点。加入队列当中

4 selfInterrupt(),自己给自己一个中断。

 

tryAcquire

这个方法在AbstractQueuedSynchronizer中是一个抽象的方法,具体实现就是子类中,这里就是在

ReentrantLock的FairSync类里面。
protected final boolean tryAcquire(int acquires) {
       1.获取当前线程
final Thread current = Thread.currentThread();
       2.当前state值
int c = getState(); if (c == 0) { //如果c等于0,表示当前没有线程占有锁,可以直接获取锁 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { //如果队列中前面没有其它等待的线程,就把状态state值设置为1acquires值,这里是1. setExclusiveOwnerThread(current); //把当前锁的持有线程设置为当前线程 return true; } } else if (current == getExclusiveOwnerThread()) { //如果c不是0,说明已经有线程持有这个锁,就看持有锁的线程是不是当前线程,如果是就把state的值加1。然后更新它的值 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
hasQueuedPredecessors
public final boolean hasQueuedPredecessors() {
        Node t = tail; // 队列的尾
        Node h = head; // 队列的头
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

这里主要的判断逻辑,就是如果头和尾不相等,说明这个队列就不为空,并且队列的头的下一下节点不是当前线程,就说明队列中有前继节点。

但是我第一次看这个代码的时候,为什么头节点的next为null的时候,也说明有前继节点呢,

 

compareAndSetState

这个一个CAS操作,就是原子地设置state的值,如果期望值是0,就设置state的值为传入的值。
setExclusiveOwnerThread
这个方法是设置当前锁的持有线程为当前线程,这个方法是在AbstractOwnableSynchronizer抽象类里定义的,它是一个表示这一个锁是由那个线程独占持有的辅助类。
AbstractQueuedSynchronizer类继承了这个类。


getExclusiveOwnerThread

如果试图获取锁不成功,就进行下一步,首先就是把这个线程加入到队列中,

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
这个方法的入参是Node.EXCLUSIVE。表示这个节点正在以锁是排它的


































以上是关于Java锁及AbstractQueuedSynchronizer源码分析的主要内容,如果未能解决你的问题,请参考以下文章

java 偏向锁轻量级锁及重量级锁synchronized原理

乐观锁和悲观锁及CAS实现

并发安全性之Lock锁及原理分析

Linux用户锁定解锁及锁定查看

锁及事务

并发安全性之Lock锁及原理分析