学习笔记【多线程-第十二节:AQS】

AQS(Abstract Queued Synchronizer)
抽象的队列的同步器,AQS定义了一套多线程访问共享资源的同步器,很多同步类底层都是它,如ReentrantLock、Semaphore、CountDownLatch…。

底层实现

拿ReentrantLock的lock()举例:
在这里插入图片描述
以上为它的底层调用图,我们可以看到,最底层AQS的acquire()方法调用的是子类重写的tryAcquire()方法,而未用自己的tryAcquire()方法,自己的tryAcquire()方法是为了给子类重写的,这其实是一种模板方法。

最后我们可以看到,到nofairTryAcquire时才是真正的调用了,它用了一个getStare(),我们再往下看看。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到,最终是一个volatile的int类型的state,而这则是模型的关键。

AQS模型

在这里插入图片描述
上面说过,AQS是很多个锁的底层实现,而state是什么,则看是什么锁实现了它。

若为ReentrantLock,则state初始为0,加锁则state加1,解锁则state减1,相当于一个计数器。

若为CountDownLatch,则state则为初始设的门栓值,若为5,则state为5,每一次latch.countDown();,state都会减1,到0时解开。

模型底下的node队列,其实时一个双向链表,每一个node中存放着线程,以及前一个及后一个的地址。
在这里插入图片描述

线程获得锁,或者等待,都要进入该队列中。我们再看看刚刚的这个源码:
在这里插入图片描述
如果c==0,会用CAS的方式去抢这一把锁,若抢到了,则当前线程独占这把锁(ReentrantLock的)。

else if那里,倘若当前线程就是正在独占锁的线程,c+acquires,便是可重入了。

面试题:为什么说AQS的底层是由volatile和CAS实现的呢?
答:AQS底层的模型则是一个int类型的state和一个存放线程的双向链表。state是由volatile修饰的,让其具备可见性,而它的作用或者说对它的修改,是由锁的类型决定的,如ReentrantLock,则是初始化0,lock()即加1,unlock()即减1;又好比说CountDownLatch,初始化锁时设的门栓值就是state的初始值,每一次latch.countDown();,state都会减1,到0时解开。而CAS则是线程尝试改state值的方式,拿ReentrantLock的unfair(非公平)举例,我看了源码,是采用了comepareAndSetState()的方法,期望值是0,尝试把它改成1,改成了则拿到了锁,并把当前线程设为独占的线程。
**注意:**无论公平与非公平,队列中的线程都是先进先出的。这里的非公平,是指有一个线程来了,先用CAS尝试去获取锁,若不成功,就进入队列中;而公平呢,则是多了一个hasQueuedPredecessors() 方法判断是否有其它线程等待获取锁的时间超过当前线程,如果有返回true,则表明同步队列中已有线程先行进行等待获取锁,然后进入等待队列。

CLH队列

一共同步队列,AQS中的同步队列不是CLH队列,是CLH队列的一个变种。
AQS中,线程在队列中是被阻塞了(为了不耗cpu资源),而正统的CLH队列,是在队列中进行自旋,CLH队列原是C#语言下的一种数据结构,后被java借鉴。

写一个简单的公平锁(未写可重入、打断等处理)

public class MyLock {
    private volatile int state=0;
    //拿到锁的线程
    private Thread lockHolder;
    //创建一个队列
    private ConcurrentLinkedQueue<Thread> queue=new ConcurrentLinkedQueue<>();

  	//state与lockHolder的set与get方法
    public int getState() {return state; }

    public void setState(int state) { this.state = state; }

    public Thread getLockHolder() { return lockHolder; }

    public void setLockHolder(Thread lockHolder) { this.lockHolder = lockHolder; }
    
	//获得锁的方法
    private boolean tryAquire(){
        Thread t=Thread.currentThread();
        int state=getState();
        //判断锁是否被占有
        if (state==0){
        	//先判断队列中是否为空,若不为空,再判断是否为队列的头节点(公平),
        	//若满足,则尝试去改state的值,都为true,则设当前线程为拿到了锁的线程
            if ((queue.size()==0||t==queue.peek())&&compareAndSwapState(0,1)){
                setLockHolder(t);
                return true;
            }
        }
        return false;
    }
    
	//lock方法
    public void lock(){
    	//尝试改值,改值成功则拿到锁
        if (tryAquire()){
            return;
        }
        //若不成功,则将当前线程加入队列中
        Thread current=Thread.currentThread();
        queue.add(current);
        for (;;){
        	//等待队列中写一个死循环,若为头节点,则再尝试改值
            if (current==queue.peek()&&tryAquire()){
                System.out.println("hold lock Thread-name:"+current.getName());
                //改值成功,将其扔出队列
                queue.poll();
                return;
            }
            //不为头节点或未成功改值,阻塞
            LockSupport.park(current);
        }
    }
    
	//unlock方法
    public void unlock(){
        Thread current=Thread.currentThread();
        //判断如果不是拿到锁的线程,抛异常
        if (current!=lockHolder){
            throw new RuntimeException("不是持有锁的线程无法释放!");
        }
        int state=getState();
        //尝试将state改为0(释放锁)
        if (compareAndSwapState(state,0)){
            System.out.println(String.format("Thread-name:%s,释放锁成功!",current.getName()));
            //改成功,将持有锁线程设为null
            setLockHolder(null);
            Thread head=queue.peek();
            //如果队列的头不为空,则唤醒头线程
            if (head!=null){
                LockSupport.unpark(head);
            }
        }
    }

	//尝试改值的方法(参考AQS源码)
    public final boolean compareAndSwapState(int oldValue,int newValue){
    	//调用unsafe类的CASint方法(AtomicInteger底层用的就是该方法)
        return unsafe.compareAndSwapInt(this,stateOffset,oldValue,newValue);
}
	//实例一个unsafe,下面就是通过unsafe反射取得上面的state值
    private static final Unsafe unsafe=Unsafe.getUnsafe();

    private static final long stateOffset;

    static {
        try {
            stateOffset=unsafe.objectFieldOffset(MyLock.class.getDeclaredField("state"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

下一篇:学习笔记【多线程-第十三节:ConcurrentHashMap】
上一篇:学习笔记【多线程-第十一节:JUC Tools】

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值