最近一直在学习asq底层部分,现在将其做一些整理如下;
AQS是什么?
其实aqs 就是AbstractQueuedSynchronizer 的简称
同时查看AbstractQueuedSynchronizer 源码的注释部分
Provides a framework for implementing blocking locks and related
synchronizers (semaphores, events, etc) that rely on
first-in-first-out (FIFO) wait queues.
大致意思:提供一个框架来实现阻塞锁和相关的依赖于的同步器(信号量、事件等)先进先出(FIFO)等待队列。
我们也可以看到AbstractQueuedSynchronizer 是抽象类,那么我对它的理解就是能够帮助我们实现阻塞和同步器框架;
其ReentrantLock就是比较明显的实现类,其实java.concurrent.util包而juc当中的大多数同步器实现都是围绕AQS进行的,其都有着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等
ReentrantLock
ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步 手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。而且它具有比 synchronized更多的特性,比如它支持
-
阻塞等待队列
-
共享/独占
-
公平/非公平
-
可重入
-
允许中断
其实ReentrantLock并不是直接继承AQS来进行保持同步的,它内部有着一个内部类Sync,而这个Sync,对于Juc下面,比如 CountDownLatch Semaphore的实现也是有的
ReentrantLock实现公平与非公平性
在ReentrantLock内部定义了一个Sync的内部类,该类继承AbstractQueuedSynchronized,对 该抽象类的部分方法做了实现;并且还定义了两个子类:
1、FairSync 公平锁的实现
2、NonfairSync 非公平锁的实现 这两个类都继承自Sync,也就是间接继承了AbstractQueuedSynchronized,所以这一个 ReentrantLock同时具备公平与非公平特性。
AQS 的重要属性
// 头结点,你直接把它当做 当前持有锁的线程 可能是最好理解的
private transient volatile Node head;
// 阻塞的尾节点,每个新的节点进来,都插入到最后,也就形成了一个链表
private transient volatile Node tail;
// 这个是最重要的,代表当前锁的状态,0代表没有被占用,大于 0 代表有线程持有当前锁
// 这个值可以大于 1,是因为锁可以重入,每次重入都加上 1
private volatile int state;
// 代表当前持有独占锁的线程,举个最重要的使用例子,因为锁可以重入
// reentrantLock.lock()可以嵌套调用多次,所以每次用这个来判断当前线程是否已经拥有了锁
// if (currentThread == getExclusiveOwnerThread()) {state++}
private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer
static final class Node {
// 标识节点当前在共享模式下
static final Node SHARED = new Node();
// 标识节点当前在独占模式下 Semaphore会用到
static final Node EXCLUSIVE = null;
// ======== 下面的几个int常量是给waitStatus用的 ===========
/** waitStatus value to indicate thread has cancelled */
// 代码此线程取消了争抢这个锁
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
// 官方的描述是,其表示当前node的后继节点对应的线程需要被唤醒
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
// 条件等待
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
// 广播
static final int PROPAGATE = -3;
// =====================================================
// 标记当前节点的信号量状态 (1,0,‐1,‐2,‐3)5种状态
// 使用CAS更改状态,volatile保证线程可见性,高并发场景下
// 即被一个线程修改后,状态会立马让其他线程可见。
volatile int waitStatus;
// 前驱节点的引用
volatile Node prev;
// 后继节点的引用
volatile Node next;
// 节点同步状态的线程
volatile Thread thread;
//也就是说节点类型(独占和共享)和等待队列中的后继节点共用同一个字段。
Node nextWaiter;
}
同步等待队列/条件等待队列
同步等待队列
CLH队列 基于双向链表数据结构的队列 是FIFO先入先出线程等待队列,Java中的CLH 队列是原CLH队列的一个变种,线程由原自旋机制改为阻塞机制。
条件等待队列
Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备时,这些等待线程才会被唤醒,从而重新争夺锁
//公平锁的分析
private static ReentrantLock lock = new ReentrantLock(true);
lock.lock();
我画的导图
https://www.processon.com/view/link/60501e64637689019dde2f0e
lock()/ lockInterruptibly()
lockInterruptibly()// 如果线程阻塞 是被中断了,不会获得锁,会产生异常
lock(); //获得锁定,即使调用了线程的interrupt()方法,也没有真正的中断线程
为什么lockInterruptibly不会获取锁,返回异常?
查看源码得知:
BlockingQueue
查看官方文档可以知道其为线程通信的一个工具,在任意时刻,单jvm同一时间只有一个线程可以进行take或者put操作
DelayQueue
那么它下面有一个实现类DelayQueue,通过名字可以知道它是延迟的队列,同时看了它的源码可知其通过PriorityQueue来实现,也用到了ReentrantLock;
当然我在在建立放对象的队列时发现需要放入实现Delayed接口(Delayed实现了Comparable接口)的对象;需要实现一个getDelay的方法;
Delayed接口
/**
* Returns the remaining delay associated with this object, in the
* given time unit.
*
* @param unit the time unit
* @return the remaining delay; zero or negative values indicate
* that the delay has already elapsed
*/
long getDelay(TimeUnit unit);
Comparable接口
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
*
* <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
* -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This
* implies that <tt>x.compareTo(y)</tt> must throw an exception iff
* <tt>y.compareTo(x)</tt> throws an exception.)
*
* <p>The implementor must also ensure that the relation is transitive:
* <tt>(x.compareTo(y)>0 && y.compareTo(z)>0)</tt> implies
* <tt>x.compareTo(z)>0</tt>.
*
* <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
* implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
* all <tt>z</tt>.
*
* <p>It is strongly recommended, but <i>not</i> strictly required that
* <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>. Generally speaking, any
* class that implements the <tt>Comparable</tt> interface and violates
* this condition should clearly indicate this fact. The recommended
* language is "Note: this class has a natural ordering that is
* inconsistent with equals."
*
* <p>In the foregoing description, the notation
* <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
* <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
* <tt>0</tt>, or <tt>1</tt> according to whether the value of
* <i>expression</i> is negative, zero or positive.
*
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*
* @throws NullPointerException if the specified object is null
* @throws ClassCastException if the specified object's type prevents it
* from being compared to this object.
*/
public int compareTo(T o);
从我使用我理解到的意思是返回倒计时时间
从下面的源码可以看出
而DelayQueue先按照Comparable接口对所有元素进行排序,一般情况下,我们都是按元素过期时间的优先级进行排序。这里,即按照倒计时时间短进行排序;
eg:
public class Mian2 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<DelayedElement > delayeds = new DelayQueue<DelayedElement >();
String strDateFormat = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
System.out.println(sdf.format(System.currentTimeMillis()));
DelayedElement element1 = new DelayedElement(5000);
delayeds.offer(element1);
DelayedElement element2 = new DelayedElement(10000);
delayeds.offer(element2);
DelayedElement element3 = new DelayedElement(15000);
delayeds.offer(element3);
DelayedElement element4 = new DelayedElement(20000);
delayeds.offer(element4);
new Thread(()-> {
while (true){
DelayedElement elementt = null;
try {
elementt = delayeds.take();
System.out.println(sdf.format(System.currentTimeMillis())+elementt);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
class DelayedElement implements Delayed{
private long delay ;
private final long expire;
public DelayedElement (long delay) {
this.expire = System.currentTimeMillis() + delay;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) -o.getDelay(TimeUnit.MILLISECONDS));
}
}