浅析AQS框架及实现原理

先从 Lock 讲起:

Lock

同步锁Lock是解决线程安全问题的常用手段,在并发包 java.util.concurrent 中。

Lock 是一个接口,具体方法如下:

void lock();  //线程获取锁,如果锁不可用,则阻塞休眠,直到获得锁被唤醒
void lockInterruptibly();  //与lock()方法类似,但阻塞的线程可中断,并抛出异常
boolean tryLock();  // 非阻塞线程尝试获取锁,获取成功返回true
boolean tryLock(long time, TimeUnit unit);  //带有超时时间的获取锁方法
void unlock();  //释放锁

Lock的实现

  • ReentranLock:可重入锁,是唯一一个直接实现了Lock接口的类。可重入的意思是,线程在释放锁后,可不需阻塞直接再次获得锁。不仅判断锁有没有被锁上,还会判断锁是谁锁上的,当就是自己锁上的时候,那么他依旧可以再次访问临界资源,并把加锁次数加一。设计了加锁次数,以在解锁的过程中,保证所有的过程都解锁。
  • ReentrantReadWriteLock:可重入读写锁,实现了 ReadWriteLock 接口。内部维护了 ReadLock和WriteLock两个锁,都实现了Lock接口,读写锁是一种适合读多写少的场景下解决线程安全问题的工具,基本原则:读和读不互斥,读和写互斥,写和写不互斥。即影响到数据变化的操作都会存在互斥。

AQS源码分析

AQS全称 AbstractQueuedSynchronizer,是一个抽象类,又称同步序列器。内部提供了一个FIFO队列,AQS可以看作 用来实现同步锁 以及 一系列依赖FIFO等待队列的同步功能的核心组件。

AQS为并发包中的同步工具类提供了通用的机制

AQS的内部实现原理:AQS的实现依赖内部的同步队列,即FIFO双向队列(先进先出),这种结构的特点是每个节点都有两个指针,分别指向直接的前驱节点和直接的后置节点。

AQS使用一个int类型的成员变量state来表示线程的同步状态,当state>0时表示已经获取了锁,当state = 0时表示释放了锁。它提供了三个方法(getState()、setState(int newState)、compareAndSetState(int expect,int update))来对同步状态state进行操作。

因为ReentrantLock允许重入,所以同一个线程多次获得同步锁的时候,state会递增,比如重入5次,那么state=5。 而在释放锁的时候,同样需要释放5次直到state=0其他线程才有资格获得锁

private volatile int state;

protected final int getState() {
    return state;
}

protected final void setState(int newState) {
    state = newState;
}

内部类Node类组成:

static final class Node {
        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile Node prev; //前驱节点
        volatile Node next; //后继节点
        volatile Thread thread;//当前线程
        Node nextWaiter; //存储在condition队列中的后继节点
        //是否为共享锁
        final boolean isShared() { 
            return nextWaiter == SHARED;
        }

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

        Node() {    // Used to establish initial head or SHARED marker
        }
        //将线程构造成一个Node,添加到等待队列
        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }
        //这个方法会在Condition队列使用
        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

锁竞争添加节点:

释放锁移除节点:

AQS中还有很多方法,下面结合ReentranLock进行讲解:

ReentranLock源码分析:

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

这是获取锁的入口,里面通过调用sync的 lock()方法 来获取锁。

abstract static class Sync extends AbstractQueuedSynchronizer

Sync类是ReentranLock里的静态内部类,继承了AQS这个抽象类,前面说过AQS是一个同步工具,主要用来实现同步控制。我们在利用这个工具的时候,会继承它来实现同步控制功能。;

Sync这个类有两个具体的实现,分别是 NofairSync(非公平锁),FailSync(公平锁):

  • 公平锁 表示所有线程严格按照FIFO来获取锁
  • 非公平锁 表示可以存在抢占锁的功能,也就是说不管当前队列上是否存在其他线程等待,新线程都有机会抢占锁

ReentranLock 默认为非公平锁,下面以非公平锁作主要分析。

NonfairSync.lock:

final void lock() {
    // 非公平锁,通过CAS去抢占锁
    if (compareAndSetState(0, 1)) 
      // 抢占成功,保存获得锁的当前线程
      setExclusiveOwnerThread(Thread.currentThread());
    else
      acquire(1); // 走锁竞争逻辑,尝试去获取锁
}

acquire 是继承自AQS的方法,如果CAS操作未成功,表示当前state的值为0,acquire(1) 表示 锁竞争,想再次把线程状态置为1.

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

通过tryAcquire尝试获取独占锁,如果tryAcquire失败,则会通过addWaiter方法将当前线程封装成Node添加到AQS队列尾部。

acquireQueued,将Node作为参数,通过自旋去尝试获取锁。

tryAcquire 是重写AQS中的tryAcquire方法,作用是尝试获取锁,成功则返回true。

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

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值