AQS你真的学会了,深入源码分析

简介

AQS:AbstractQueuedSynchronizer,是JUC下lock包的一个类,能够多线程访问共享资源的同步器框架。

首先介绍AQS中一些重要的参数:
state:同步器的状态,
exclusiveOwnerThread:互斥锁持有的线程
CLH队列:这是一个双向链表队列
head:同步等待期的头部
tail:同步等待器的尾部

在AQS中线程访问资源有两种方式:
使用独占锁:顾名思义每次访问只能有一个线程持有锁,ReentrantLock就是以独占方式实现的互斥锁。
使用共享锁:共享锁就是允许多个线程痛死获得锁,并发访问资源。比如:ReadWriteLock

ReentrantLock可重入锁:
线程在ReentrantLock的lock方法后,获取到锁没有释放锁,当当前线程再次调用lock方法时,并不会阻塞,只会增加重入的次数。
首先我们通过一段代码逐步分析AQS的源码:

public class AQS {
    private ReentrantLock lock = new ReentrantLock();
    public void lock1(){
        lock.lock();
        System.out.println(Thread.currentThread().getName() +"获得了锁");
        lock2();
        lock.unlock();
    }

    public void lock2(){
        lock.lock();
        System.out.println(Thread.currentThread().getName() +"获得了锁");
        lock.unlock();
    }
    public static void main(String[] args) {
        AQS t = new AQS();
        Thread t1 = new Thread(()->t.lock1(),"t1");
        t1.start();
    }
}

在这里插入图片描述
从代码的执行结果来看,t1线程在调用lock1方法后并没有释放锁就调用了,lock2方法,但是并没有出现阻塞现象。
AQS源码分析:
我们根据上述代码逐步分析:

//默认的无参构造器
  public ReentrantLock() {
        sync = new NonfairSync();
    }
//有参构造器
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

首先看一下ReentrantLock类的构造方法,可以看出关键的类sync(抽象类Sync也是ReentrantLock类中的属性),这也就引出了公平锁和非公平锁的概念

//抽象类Sync继承了AbstractQueuedSynchronizer(也就是常说的AQS)
abstract static class Sync extends AbstractQueuedSynchronizer 

在这里插入图片描述
此处我们可以先熟悉一下ReentrantLock和AbstractQueuedSynchronizer的相关的结构:

跟着上述demo,我们继续调试Lock()方法:

public void lock() {
    sync.lock();
  }
 //非公平
 final void lock() {
	 if (compareAndSetState(0, 1))
	        setExclusiveOwnerThread(Thread.currentThread());
	    else
	        acquire(1);
	}
//公平
final void lock() {
    acquire(1);
    }

我们通过前面可以知道,ReentrantLock的空构造方法是构造一个非公平锁的Sync实体类:

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

所以上面代码的lock()方法,执行的是NonfairSync类中的lock()方法。

compareAndSetState(0, 1)

这个就是我们常说的CAS,
CAS相关源码:

protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
  //获取state在内存中的偏移量
  stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state")); 

这个方法底层调用的是,unsafe类下的compareAndSwapInt()方法,参数中stateOffset是state变量在内存中的偏移量,通过这个偏移量可以获取到state的值,unsafe类中的方法都具有原子性,保证了线程安全,compareAndSwapInt()方法,如果state==expect,那么就将state更新为update的值,并返回true;一开始state的值为0,传入的expect为0,update为1,此时state等于expect,则把state赋值为1,并返回true。

在非公平的lock()方法中,首先会尝试使用CAS方式加锁,如果成功了就使用

setExclusiveOwnerThread(Thread.currentThread())
//AbstractOwnableSynchronizer类下的
protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    } 

方法,将当前线程赋给exclusiveOwnerThread,这也是非公平锁的含义,每个线程加锁时,会先尝试加锁,如果成功就赋给exclusiveOwnerThread,也就不用放在CLH队列中进行排队。

CLH同步队列:是一个FIFO双向队列,AQS主要是通过其实现公平锁。线程通过AQS获取锁失败就会将线程封装成一个Node节点,插入队列尾部,当有线程释放锁时,就会尝试将锁头的next节点占用锁。
在这里插入图片描述

//首先tryAcquire获取锁,如果获取到,!tryAcquire()为false,后面不用执行,直接执行业务代码
//如果为获取到锁,添加一个新的节点,插入到CLH队列尾部
public final void acquire(int arg) {
 if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
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;
}
//*****核心:主要功能是完成头节点尝试获取锁资源,以及其他节点被阻塞
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
            	//获取当前节点的前一个节点
                final Node p = node.predecessor();
                //只有P节点是头节点才可以获取锁资源,如果获取到锁资源也就不需要阻塞
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //p节点不是第一个节点,或者p节点尝试加锁失败,
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
                // 阻塞当前节点,等待被unpark()方法唤醒   
				/**private final boolean parkAndCheckInterrupt() {
				        LockSupport.park(this);
				        return Thread.interrupted();
				    }
				**/
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
//在CLH的队列结尾处添加一个独占尾节点
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;
        //判断当前尾节点是否为null,不为null说明队列中有节点
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
            	//尾插的方式插入当前节点
                pred.next = node;
                return node;
            }
        }
        //如果对了为空,将队列初始化后插入当前节点
        enq(node);
        return node;
    }
//这是为了确保节点插入队列的尾部,防止CAS失败操作,也会保证节点插入队列
private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            //尾节点为空,也就是队列为空
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
            //此时CLH队列不为空,尾插
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

公平锁和非公平锁的区别就是在tryAcquire()方法上,多了一个hasQueuedPredecessors()方法。这个方法主要是寻找CLH队列中有没有,比当前节点等待获取锁的时间更长。

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值