AQS源码分析

1. lock方法

final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

2. acquire方法

tryAcquire是尝试获取锁,判断state是否为0和是否是锁重入,是就返回true,不是就返回false接着下面判断

addWaiter是构造等待的队列,第一个是哨兵节点,其他是等待节点

acquireQueued传入当前node,通过不断循环判断,看是否是老二而且能够获得锁,能就返回打断值,看前一个节点是否是-1状态,是就执行等待方法,直到unpark或者打断,返回是否被打断

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&    // 尝试获取锁成功了方法就结束,失败了会进入下面的方法
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 判断是否是老二和重试和...
            selfInterrupt();  // 只有获取锁失败并且等待中时被打断了的才执行这个方法
    }

2.1. tryAcquire方法

尝试获取锁,分三类,一类是state为0,用CAS实现第一次锁的获取,并且设置排他主节点,第二类是锁重入,将c+1,第三类就是其他线程抢不到锁,返回false

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

2.2.1. addWaiter

该方法第一次建立队列用的是enq方法,以后都直接在if语句里面就return了,总体建立的是双向链表

方法返回值是新增的节点

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

2.2.1.1. enq

第一次建立队列用的方法,加哨兵节点

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

2.2. acquireQueued

获得的参数是addWaiter方法返回的新增的节点和 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)) {  
                    // 第三次循环如果抢到了是在tryAcquire中设置了node节点的线程为排他主节点
                    // 如果你的前一个节点是哨兵节点,说明你是即将获取锁的那位,再次重新获取一下锁
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&  
                    // p是当前节点的前置节点,node是当前节点
                    parkAndCheckInterrupt())  // 第一个是看看要不要park,第二个是执行park
                    interrupted = true;// 第二个方法来判断是否被打断的,是的话就会执行这行设置true
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

2.2.1. predecessor

返回值是调用节点的前一个节点

final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

2.2.2. shouldParkAfterFailedAcquire

失败后是否要park

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)  // 如果是SIGNAL状态,即等待被占用的资源释放,直接返回true
            return true;
        if (ws > 0) {  // ws大于0说明是CANCELLED状态
           // 循环判断前驱节点的前驱节点是否也为CANCELLED状态,忽略该状态的节点,连接最后面<=0的节点
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            // 将当前节点的前驱节点设置为SIGNAL状态,用于后续唤醒操作
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);  // 0 -> -1
        }
        return false;
    }

2.2.3. parkAndCheckInterrupt

上面判断失败后需要park后,这个方法就把这个node 挂起了

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);   // 在这个点就停下了,一直等待别人唤醒
    // 1、被unpark
    // 2、被中断(interrupt)
    // 3、其他不合逻辑的返回才会继续向下执行
    // 因上述三种情况程序执行到此,返回当前线程的中断状态,并清空中断状态
        return Thread.interrupted(); // 如果由于被中断,该方法会返回true,其他返回false
    }

2.2.4. setHead

private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

3. unlock

public void unlock() {
        sync.release(1);
   }

4. release

tryRelease是判断减去releases后状态为是否为0,是的话就是解锁了,不是说明还不应该放开锁

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;  
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);  // 该方法是设置头节点waitState为0和unpark下一个节点
            return true;
        }
        return false;
    }

4.1. tryRelease

该方法判断的是减去releases后状态为是否为0,是的话执行下面的unparkSuccessor方法

protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 支持锁重入,只有state减为0,才释放成功
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

4.2. unparkSuccessor

执行unpark操作

private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);  // -1 -> 0
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中的AQS(AbstractQueuedSynchronizer)是实现锁和同步器的一种重要工具。在AQS中,一个节点表示一个线程,依次排列在一个双向队列中,同时使用CAS原子操作来保证线程安全。当多个线程对于同一资竞争时,一个节点会被放置在队列的尾部,其他线程则在其之前等待,直到该资可以被锁定。 当一个线程调用lock()方法进行锁定时,它会首先调用tryAcquire()方法尝试获取锁。如果当前资尚未被锁定,则该线程成功获取锁,tryAcquire()返回true。如果当前资已被锁定,则线程无法获取锁,tryAcquire()返回false。此时该线程就会被加入到等待队列中,同时被加入到前一个节点的后置节点中,即成为它的后继。然后该线程会在park()方法处等待,直到前一个节点释放了锁,再重新尝试获取锁。 在AQS中,当一个节点即将释放锁时,它会调用tryRelease()方法来释放锁,并唤醒后置节点以重试获取锁。如果当前节点没有后置节点,则不会发生任何操作。当一个线程在队列头部成功获取锁和资时,该线程需要使用release()方法释放锁和资,并唤醒等待队列中的后置节点。 总之,AQS中的锁机制是通过双向等待队列实现的,其中节点表示线程,使用CAS原子操作保证线程安全,并在tryAcquire()和tryRelease()方法中进行锁定和释放。该机制保证了多线程环境下资的正确访问和线程的安全执行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值