AQS-ReentrantLock

AQS

        AQS(AbstractQueuedSynchronizer)定义了一套多线程访问共享资源的同步器框架,是一个依赖状态(state)的同步器。Java并发编程核心java.concurrent.util包当中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这个行为的抽象就是基于AQS。例如Semaphore、CountDownLatch、ReentrantLock、BlockingQueue等都是基AQS实现的。

AQS具备特性: 

  • 阻塞等待队列
  • 共享/独占
  • 公平/非公平
  • 可重入
  • 允许中断

AQS中几个关键的概念

state:状态器

表示资源的可用状态(同步状态),线程获取锁、释放锁时需要修改,通过CAS完成对state值的修改

state=1 : 表示锁被某个线程占用了

state=0 : 表示锁处于空闲的状态

waitStatus

当前节点在队列中所处的状态,有5种值(默认值0)

0:初始状态,当一个Node被初始化的时候的默认值

CANCELLED:1,表示节点线程获取锁的请求已经取消了

CONDITION:-2,表示节点在等待队列中,节点线程等待唤醒

PROPAGATE:-3,当前节点线程处在SHARED情况下,该字段才会使用

SIGNAL:-1,表示下一个链接的节点线程可以被唤醒

 
AQS定义资源访问的两种方式
 
Exclusive-独占:只有一个线程能访问资源,如ReentrantLock
Share-共享:多个线程可同时执行访问,如Semaphore/CountDownLatch
AQS定义的两种队列

同步等待队列:CLH,只有在该队列中的节点才能获取锁

条件等待队列:

同步等待队列CLH

        CLH是基于双向链表数据结构的队列,是FIFO先入先出线程等待队列,Java中的CLH队列是原CLH队列的一个变种,线程由原自旋机制改为阻塞机制。AQS是通过将每个请求共享资源的线程封装成一个节点来实现锁的分配。

 

 条件等待队列

        Condition是一个多线程间协调通信的工具类,使得某个或者某些线程一起等待某个条件(Condition),只有当该条件具备时,这些等待线程才会被唤醒,从而重新争夺锁

ReentrantLock

         ReentrantLock是一种基于AQS框架的具体应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。下文将从ReentrantLock的源码角度分析其加锁和解锁的过程,来初步探寻AQS的底层精髓。希望该文章可以让大家对AQS有初步的了解,后续还会深入浅出的介绍AQS的其它实现容器。  

        在ReentrantLock内部定义了一个Sync的内部类,该类继承AbstractQueuedSynchronized,对 该抽象类的部分方法做了实现;并且还定义了两个子类:
1、FairSync 公平锁的实现
2、NonfairSync 非公平锁的实现
这两个类都继承自Sync,也就是间接继承了AbstractQueuedSynchronized,所以这一个
ReentrantLock同时具备公平与非公平特性,默认空构造为非公平锁。

公平锁的加锁过程

 

Q: 第4步中为什么获取了锁还要中断线程?

static void selfInterrupt() {
	Thread.currentThread().interrupt();
}

 A:Thread.interrupted()方法在返回true的同时会把中断状态重置为false,为了避免丢失中断状态,就需要在acquire(..)方法里重新调用一下selfInterrupt()把中断状态设置为true。

在 CLH队列中是不响应中断的,那么等他出队列了就应该把补上。线程在等待资源的过程中被唤醒,唤醒后还是会不断地去尝试获取锁,直到抢到锁为止。也就是说,在整个流程中,并不响应中断,只是记录中断记录。最后抢到锁返回了,那么如果被中断过的话,就需要补充一次中断。

 

解锁过程

        ReentrantLock在解锁的时候,并不区分公平锁和非公平锁

   public void unlock() {
        sync.release(1);
    }
//java.util.concurrent.locks.AbstractQueuedSynchronizer
//所以锁的释放来源于AQS底层提供的基础能力  
 public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            // 头结点非空并且头结点不是初始化状态,解除当前线程的挂起状态
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
// 当前锁是否被线程持有     
protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            // 只有自己才能释放自己的锁,否则抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 同步状态state=0,表示持有锁的线程全部被释放,则将当前独占锁的线程设置为null
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            // 更新state
            setState(c);
            return free;
}
 private void unparkSuccessor(Node node) {
        // 获取头结点的状态
        int ws = node.waitStatus;
        if (ws < 0)
            node.compareAndSetWaitStatus(ws, 0);
        Node s = node.next;
        //如果下一个节点是null或者下一个节点被cancelled,就找到队列最开始的非cancelled的节点
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node p = tail; p != node && p != null; p = p.prev)
                if (p.waitStatus <= 0)
                    s = p;
        }
        // 如果当前节点不为空,而且状态<=0,就被当前节点unpark
        if (s != null)
            LockSupport.unpark(s.thread);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值