Java多线程--AQS
ReentrantLock和AQS的关系
首先我们来看看,如果用java并发包下的ReentrantLock来加锁和释放锁,是个什么样的:
1 ReentrantLock reentrantLock = new ReentrantLock();
2 reentrantLock.lock();
3 //业务代码
4 reentrantLock.unlock();
上面那段代码就是搞一个Lock对象,然后加锁和释放锁。那么,这个跟AQS有啥关系?关系大了去了,因为java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,AQS是java并发包的基础类。上一部分源码:
 1 public class ReentrantLock implements Lock, java.io.Serializable {
 2     private static final long serialVersionUID = 7373984872572414699L;
 3     /** Synchronizer providing all implementation mechanics */
 4     private final Sync sync;
 5
 6     /**
 7      * Base of synchronization control for this lock. Subclassed
 8      * into fair and nonfair versions below. Uses AQS state to
 9      * represent the number of holds on the lock.
10      */
11     abstract static class Sync extends AbstractQueuedSynchronizer {
12         private static final long serialVersionUID = -5179523762034025860L;
13
14         /**
15          * Performs {@link Lock#lock}. The main reason for subclassing
16          * is to allow fast path for nonfair version.
17          */
18         abstract void lock();
19
20         /**
21          * Performs non-fair tryLock.  tryAcquire is implemented in
22          * subclasses, but both need nonfair try for trylock method.
23          */
24         final boolean nonfairTryAcquire(int acquires) {
25             final Thread current = Thread.currentThread();
26             int c = getState();
27             if (c == 0) {
28                 if (compareAndSetState(0, acquires)) {
29                     setExclusiveOwnerThread(current);
30                     return true;
31                 }
32             }
33             else if (current == getExclusiveOwnerThread()) {
34                 int nextc = c + acquires;
35                 if (nextc < 0) // overflow
36                     throw new Error("Maximum lock count exceeded");
37                 setState(nextc);
38                 return true;
39             }
40             return false;
41         }
42
43         protected final boolean tryRelease(int releases) {
44             int c = getState() - releases;
45             if (Thread.currentThread() != getExclusiveOwnerThread())
46                 throw new IllegalMonitorStateException();
47             boolean free = false;
48             if (c == 0) {
49                 free = true;
50                 setExclusiveOwnerThread(null);
51             }
52             setState(c);
53             return free;
54         }
55
56         protected final boolean isHeldExclusively() {
57             // While we must in general read state before owner,
58             // we don't need to do so to check if current thread is owner
59             return getExclusiveOwnerThread() == Thread.currentThread();
60         }
61
62         final ConditionObject newCondition() {
63             return new ConditionObject();
64         }
65
66         // Methods relayed from outer class
67
68         final Thread getOwner() {
69             return getState() == 0 ? null : getExclusiveOwnerThread();
70         }
71
72         final int getHoldCount() {
73             return isHeldExclusively() ? getState() : 0;
74         }
75
76         final boolean isLocked() {
77             return getState() != 0;
78         }
79
80         /**
81          * Reconstitutes the instance from a stream (that is, deserializes it).
82          */
83         private void readObject(java.io.ObjectInputStream s)
84             throws java.io.IOException, ClassNotFoundException {
85             s.defaultReadObject();
86             setState(0); // reset to unlocked state
87         }
88     }
89 }
说白了,ReentrantLock内部包含了一个AQS对象,也就是AbstractQueuedSynchronizer类型的对象。这个AQS对象就是ReentrantLock可以实现加锁和释放锁的关键性的核心组件。
ReentrantLock加锁和释放锁的底层原理
现在如果有一个线程过来尝试用ReentrantLock的lock()方法进行加锁,会发生什么事情呢?
1 public abstract class AbstractQueuedSynchronizer
2 extends AbstractOwnableSynchronizer
3 implements java.io.Serializable {
4
5 /**
6 * The thread that enqueued this node. Initialized on
7 * construction and nulled out after use.
8 */
9 volatile Thread thread;
10
11 /**
12 * The synchronization state.
13 */
14 private volatile int state;
15
16 }
这个AQS对象内部有一个核心的变量叫做state,是int类型的,代表了加锁的状态。初始状态下,这个state的值是0。另外,这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。接着线程1跑过来调用ReentrantLock的lock()方法尝试进行加锁,这个加锁的过程,直接就是用CAS操作将state值从0变为1。如果之前没人加过锁,那么state的值肯定是0,此时线程1就可以加锁成功。一旦线程1加锁成功了之后,就可以设置当前加锁线程是自己。
AQS就是并发包里的一个核心组件,里面有state变量、加锁线程变量等核心的东西,维护了加锁状态。ReentrantLock这种东西只是一个外层的API,内核中的锁机制实现都是依赖AQS组件的。这个ReentrantLock之所以用Reentrant打头,意思就是他是一个可重入锁。意思就是你可以对一个ReentrantLock对象多次执行lock()加锁和unlock()释放锁,也就是可以对一个锁加多次,叫做可重入加锁。大家看明白了那个state变量之后,就知道了如何进行可重入加锁!其实每次线程1可重入加锁一次,会判断一下当前加锁线程就是自己,那么他自己就可以可重入多次加锁,每次加锁就是把state的值给累加1,别的没啥变化,实现原理如下:
 1 public class ReentrantLock implements Lock, java.io.Serializable {
 2     /**
 3      * Sync object for non-fair locks
 4      */
 5     static final class NonfairSync extends Sync {
 6         private static final long serialVersionUID = 7316153563782823691L;
 7
 8         /**
 9          * Performs lock.  Try immediate barge, backing up to normal
10          * acquire on failure.
11          */
12         final void lock() {
13             if (compareAndSetState(0, 1))
14                 setExclusiveOwnerThread(Thread.currentThread());
15             else
16                 acquire(1);
17         }
18
19         protected final boolean tryAcquire(int acquires) {
20             return nonfairTryAcquire(acquires);
21         }
22     }
23 }
24
25 public abstract class AbstractQueuedSynchronizer
26     extends AbstractOwnableSynchronizer
27     implements java.io.Serializable {
28
29     /**
30      * Acquires in exclusive mode, ignoring interrupts.  Implemented
31      * by invoking at least once {@link #tryAcquire},
32      * returning on success.  Otherwise the thread is queued, possibly
33      * repeatedly blocking and unblocking, invoking {@link
34      * #tryAcquire} until success.  This method can be used
35      * to implement method {@link Lock#lock}.
36      *
37      * @param arg the acquire argument.  This value is conveyed to
38      *        {@link #tryAcquire} but is otherwise uninterpreted and
39      *        can represent anything you like.
40      */
41     public final void acquire(int arg) {
42         if (!tryAcquire(arg) &&
43             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
44             selfInterrupt();
45     }
46
47 }
接着,如果线程1加锁了之后,线程2跑过来加锁会怎么样呢?我们来看看锁的互斥是如何实现的,线程2跑过来一下看到state的值不是0,所以CAS操作将state从0变为1的过程会失败,因为state的值当前为1,说明已经有人加锁了!接着线程2会看一下,是不是自己之前加的锁啊?当然不是了,“加锁线程”这个变量明确记录了是线程1占用了这个锁,所以线程2此时就是加锁失败。接着,线程2会将自己放入AQS中的一个等待队列,因为自己尝试加锁失败了,此时就要将自己放入队列中来等待,等待线程1释放锁之后,自己就可以重新尝试加锁了,所以大家可以看到,AQS是如此的核心。AQS内部还有一个等待队列,专门放那些加锁失败的线程。
1 /**
2 * Condition implementation for a {@link
3 * AbstractQueuedSynchronizer} serving as the basis of a {@link
4 * Lock} implementation.
5 *
6 * <p>Method documentation for this class describes mechanics,
7 * not behavioral specifications from the point of view of Lock
8 * and Condition users. Exported versions of this class will in
9 * general need to be accompanied by documentation describing
10 * condition semantics that rely on those of the associated
11 * {@code AbstractQueuedSynchronizer}.
12 *
13 * <p>This class is Serializable, but all fields are transient,
14 * so deserialized conditions have no waiters.
15 */
16 public class ConditionObject implements Condition, java.io.Serializable {
17 private static final long serialVersionUID = 1173984872572414699L;
18 /** First node of condition queue. */
19 private transient Node firstWaiter;
20 /** Last node of condition queue. */
21 private transient Node lastWaiter;
22
23 /**
24 * Creates a new {@code ConditionObject} instance.
25 */
26 public ConditionObject() { }
27
28 // Internal methods
29
30 /**
31 * Adds a new waiter to wait queue.
32 * @return its new wait node
33 */
34 private Node addConditionWaiter() {
35 Node t = lastWaiter;
36 // If lastWaiter is cancelled, clean out.
37 if (t != null && t.waitStatus != Node.CONDITION) {
38 unlinkCancelledWaiters();
39 t = lastWaiter;
40 }
41 Node node = new Node(Thread.currentThread(), Node.CONDITION);
42 if (t == null)
43 firstWaiter = node;
44 else
45 t.nextWaiter = node;
46 lastWaiter = node;
47 return node;
48 }
49 }
接着,线程1在执行完自己的业务逻辑代码之后,就会释放锁,他释放锁的过程非常的简单,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null!
 1 public class ReentrantLock implements Lock, java.io.Serializable {
 2     /**
 3      * Attempts to release this lock.
 4      *
 5      * <p>If the current thread is the holder of this lock then the hold
 6      * count is decremented.  If the hold count is now zero then the lock
 7      * is released.  If the current thread is not the holder of this
 8      * lock then {@link IllegalMonitorStateException} is thrown.
 9      *
10      * @throws IllegalMonitorStateException if the current thread does not
11      *         hold this lock
12      */
13     public void unlock() {
14         sync.release(1);
15     }
16 }
17
18 public abstract class AbstractQueuedSynchronizer
19     extends AbstractOwnableSynchronizer
20     implements java.io.Serializable {
21      public final boolean release(int arg) {
22         if (tryRelease(arg)) {
23             Node h = head;
24             if (h != null && h.waitStatus != 0)
25                 unparkSuccessor(h);
26             return true;
27         }
28         return false;
29     }
30 }
接下来,会从等待队列的队头唤醒线程2重新尝试加锁。好!线程2现在就重新尝试加锁,这时还是用CAS操作将state从0变为1,此时就会成功,成功之后代表加锁成功,就会将state设置为1。此外,还要把“加锁线程”设置为线程2自己,同时线程2自己就从等待队列中出队了。
其实一句话总结:AQS就是一个并发包的基础组件,用来实现各种锁,各种同步组件的。它包含了state变量、加锁线程、等待队列等并发中的核心组件。
Java多线程--AQS的更多相关文章
- Java多线程——AQS框架源码阅读
		AQS,全称AbstractQueuedSynchronizer,是Concurrent包锁的核心,没有AQS就没有Java的Concurrent包.它到底是个什么,我们来看看源码的第一段注解是怎么说 ... 
- Java多线程:AQS
		在Java多线程:线程间通信之Lock中我们提到了ReentrantLock是API级别的实现,但是没有说明其具体实现原理.实际上,ReentrantLock的底层实现使用了AQS(AbstractQ ... 
- Java多线程系列--AQS之 LockSupport
		concurrent包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS(JAVA CAS原理.unsafe.AQS)框架借助于两个类: Unsafe(提供CAS操作 ... 
- Java多线程并发06——CAS与AQS
		在进行更近一步的了解Java锁的知识之前,我们需要先了解与锁有关的两个概念 CAS 与 AQS.关注我的公众号「Java面典」了解更多 Java 相关知识点. CAS(Compare And Swap ... 
- Java多线程专题4: 锁的实现基础 AQS
		合集目录 Java多线程专题4: 锁的实现基础 AQS 对 AQS(AbstractQueuedSynchronizer)的理解 Provides a framework for implementi ... 
- 40个Java多线程问题总结
		前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ... 
- Java多线程系列--“JUC锁”03之 公平锁(一)
		概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ... 
- Java多线程系列--“JUC锁”04之 公平锁(二)
		概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ... 
- Java多线程系列--“JUC锁”01之 框架
		本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ... 
随机推荐
- GRMS_README
			基于Hadoop的商品推荐系统 基于特征:基于行为:具有了一定的历史特征. 基于用户: 基于商品: 推荐结果=用户的购买向量*物品的相似度矩阵 物品的相似度:物品的共现次数 1.项目名:GRMS2.添 ... 
- Cinder Volume 服务启动流程分析和周期性任务分析
			1.cinder-volume服务的程序入口 #!/usr/bin/python2 # PBR Generated from u'console_scripts' import sys from ci ... 
- springSecurity初识-练气初期
			1.写在前面 Spring Security是一个框架,提供针对常见攻击的身份验证,授权和保护.通过对命令式和反应式应用程序的一流支持,它是保护基于Spring的应用程序的事实标准. Spring S ... 
- QPS过万,redis大量连接超时怎么解决?
			7月2号10点后,刚好某个负责的服务发生大量的redis连接超时的异常(redis.clients.jedis.exceptions.JedisConnectionException),由于本身的数据 ... 
- A Case for Lease-Based, Utilitarian Resource Management on Mobile Devices
			郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 以下是对本文关键部分的摘抄翻译,详情请参见原文. Abstract 移动应用程序已经成为我们日常生活中不可或缺的一部分,但许多应用程序的设 ... 
- Python开发的入门教程(四)-dict
			介绍 本文主要介绍Python中dict的基本知识和使用. Python之什么是dict 我们已经知道,list 和 tuple 可以用来表示顺序集合,例如,班里同学的名字: ['Adam', 'Li ... 
- Java多线程_生产者消费者模式2
			在我的上一条博客中,已经介绍到了多线程的经典案列——生产者消费者模式,但是在上篇中用的是传统的麻烦的非阻塞队列实现的.在这篇博客中我将介绍另一种方式就是:用阻塞队列完成生产者消费者模式,可以使用多种阻 ... 
- 问题定位 | Peronca Xtrabackup 8.0近日踩坑总结 - xtrabackup 2.4和8.0区别
			目录 前言 适配过程中遇到的坑 xtrabackup 2.4和8.0区别 问题定位 坑一:MySQL 8.0 + Semi-Sync 重建问题 坑二:MySQL 8.0 + Group-Replica ... 
- Labview学习之路(八)如何让控件显示在修饰符的前面
			在Labview2017版本中,前面板选择修饰控件,会出现部分修饰控件会掩盖其他控件,情况如下: 我们右键点击和属性中都没有相关属性的改变,为什么是这样我也不清除: 上网查了一下,看到其他版本会有显示 ... 
- 关于idea中SpringBoot启动失败的坑
			很多时候你新建了Maven 或者SpringBoot 工程,激动的点了主启动类,你就发现了下面的错误 Error starting Tomcat context. Exception: org.spr ... 
