欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java并发-----AQS(1)

程序员文章站 2022-11-30 13:42:38
AQS AbstractQueuedSynchronizer同步器,这也是ReentranLock实现锁的基础,如果不知道AQS,就很难理解锁到底是怎么实现的,当然这其中不包括通过synchronize获取的锁,当然我们入手点当然是ReentrantLock,通过ReentranLock lock = new ReentrantLock();一般来讲我们通过lock.lock()来获取锁,ReentrantLockpublic void lock() { sync.lock......

AQS AbstractQueuedSynchronizer同步器,这也是ReentranLock实现锁的基础,如果不知道AQS,就很难理解锁到底是怎么实现的,当然这其中不包括通过synchronize获取的锁,当然我们入手点当然是ReentrantLock,通过ReentranLock lock = new ReentrantLock();

一般来讲我们通过lock.lock()来获取锁,

ReentrantLock
public void lock() {
        sync.lock();
   }

他调用的是同步器的lock方法,所以下面我们看看到底是怎样实现的

同步器及其实例

private final Sync sync;

static final class NonfairSync extends Sync 

public ReentrantLock() {
        sync = new NonfairSync();
    }

所以根据上面的代码可以知道,在默认构造方法里,实例化的非公平同步器NonfairSync,lock里调用的是nonfairSync里面的lock方法,

 
非公平同步器lock方法
final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

 

首先他会通过CAS来吧state从0变成1,如果成功了,就说明以前state的状态为0,由于state是有volatile修饰的,对其他线程是可见的,一修改,别的线程就知道,一旦compareAndSetState(0, 1)返回的是true,意味着其中线程不会执行setExclusiveOwnerThread(Thread.currentThread());知道state变为0,所以执行setExclusiveOwnerThread(Thread.currentThread());是线程安全安全的,而该方法只要是记录当前线程为执行线程。

如果compareAndSetState(0, 1)返回false就会走acquire(1);这个方法是AbstractQueueSynchronizer的

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

首先会判断!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg),首先会执行tryAcquire(1)

这个方法在nonfairSync里面

protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

他会执行nonfairTryAcquire,然后再一次尝试获取锁,当然如果有线程获取锁,这里hi不可能成功的,如果正好有线程执行完,他就能获得锁,不用去等待队列了,然后会判断当前线程是不是正在执行独占锁的线程,如果是就是锁重入,虽然此时state大于0,但是还是会加1,然后返回true,这样的!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)就是tfalse,acquire方法就不会干啥了,如果state != 0,而且此时执行的线程不是当前线程,就会直接返回false,这样的话就会走acquireQueued(addWaiter(Node.EXCLUSIVE), 1);首先他会执行addWaiter(Node.EXCLUSIVE)。

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;
}

把当前线程包装成一个节点,如果当前等待队列的尾节点不为空,并把节点插入等待队列末尾。

在多多线程环境下,就会有两种情况没有解决,一种是首尾节点都为null,另一种是多个线程执行到node.prev = pred;这就会造成多个节点的pred指向尾节点,但是只有一个成功,还有许多没有成功的,这两种情况就会进入enq(node);

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

在这个方法里,就会干两件事,对于首尾节点为空的情况,就会通过一个空节点把首尾节点关联起来,然后进行第二步,第二步就是通过无限循环的情况,把节点插入等待队列的末尾,这样就解决的上述的两种情况。然后把现在尾节点返回。

然后就会执行acquireQueued(node,1)当然,第一个参数就是尾节点,

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                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);
        }
    }

如果尾节点是首节点的下一个节点,就会再一次的获取锁,如果能获取锁,等待队列就不需要记录该节点的内容,就会把该节点设置为当前首节点,然后把前首节点的引用都设置为空,好让他被jvm回收,然后返回false,

如果当前尾节点不是首节点的下一个节点,就会进入

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

判断当前线程包装的结点的前节点的waitStatus,如果前节点的等待状态是SIGNAL,就返回true,当他的状态大于0,就会把前节点干掉,直接把前节点的前节点和当前线程的包装节点关联起来,如果前面都没有走前面的,就会把前节点的等待状态改为SIGNAL,其实只要前节点的等待状态不为SIGNAL,就会返回false。只要前节点的等待状态为SIGNAL,就进入parkAndCheckInterrupt()

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

把当前线程进入阻塞,返回他的中断方法,

 

本文地址:https://blog.csdn.net/tangjingui/article/details/107142656