锁在不同角度上进行划分: 如何实现自旋锁 如何实现可重入锁:一个线程的多个流程能不能重复获取同一个把锁
Lock体系:
ReentrantLock 是一种显示锁,需要手动加锁和解锁 并且ReentrantLock 是可重入锁 ,并且可以通过构造函数 指定创建的ReentrantLock 锁是否为公平锁;
什么是可重入:一个线程的多个流程都能够获取到同一把锁。
ReentrantLock 可重入锁的源码分析
AQS 源码
AQS 特性:
Java.concurrent.util当中同步器的实现如Lock,Latch,Barrier等,都是基于AQS框架实现
一般通过定义内部类Sync继承AQS
将同步器所有调用都映射到Sync对应的方法
AQS内部维护属性volatile int state (32位) 该变量用于记录 锁被加了多少次 和 被释放了多少次等等; state表示资源的可用状态
State三种访问方式 实现可重入
getState()、setState()、compareAndSetState()
AQS定义两种资源共享方式 exclusiveOwnerThread 实现 共享/独占
Exclusive-独占,只有一个线程能执行,如ReentrantLock
Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch
AQS定义两种队列 :通过内部类 Node 实现
同步等待队列 数据结构为 双向链表
条件等待队列 数据结构为 单向链表
Node结点是对每一个访问同步代码的线程的封装,其包含了需要同步的线程本身以及线程的状态,如是否被阻塞,是否等待唤醒,是否已经被取消等。
变量waitStatus则表示当前被封装成Node结点的等待状态,共有4种取值cancelled(取消)、signal(标志)、condition(条件)、propagate(传播)。
CANCELLED:值为1,在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点,其结点的waitStatus为CANCELLED,即结束状态,进入该状态后的结点将不会再变化。
SIGNAL:值为-1,被标识为该等待唤醒状态的后继结点,当其前继结点的线程释放了同步锁或被取消,将会通知该后继结点的线程执行。说白了,就是处于唤醒状态,只要前继结点释放锁,就会通知标识为SIGNAL状态的后继结点的线程执行。 标志为下个等待被唤醒的线程节点
CONDITION:值为-2,与Condition相关,该标识的结点处于条件队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从条件队列转移到同步队列中,等待获取同步锁。
PROPAGATE:值为-3,与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态。
AQS中同步队列采用的是用双向链表保存,用prve和next相互链接。
AQS中条件队列是使用单向列表保存的,用nextWaiter来连接。同步队列和条件队列并不是使用的相同的数据结构。
static final class Node { static final AbstractQueuedSynchronizer.Node SHARED = new AbstractQueuedSynchronizer.Node(); //声明共享模式 static final AbstractQueuedSynchronizer.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; // waitStatus是当前节点的一个等待状态标志位,该标志位决定了该节点在当前情况下处于何种状态。 volatile AbstractQueuedSynchronizer.Node prev; //前继节点 volatile AbstractQueuedSynchronizer.Node next; //后继节点 通过前继节点和后继节点来实现 同步队列 volatile Thread thread; // 线程 AbstractQueuedSynchronizer.Node nextWaiter; //下一个等待的线程 通过该节点实现条件队列 final boolean isShared() { // 判断该节点是否为 共享 return this.nextWaiter == SHARED; } // 该方法用来查找前置节点是否存在,相当于为前置节点查空。 final AbstractQueuedSynchronizer.Node predecessor() throws NullPointerException { AbstractQueuedSynchronizer.Node var1 = this.prev; if (var1 == null) { throw new NullPointerException(); } else { return var1; } } // 构造方法为空参构造,一般用于创建head节点,或者为nextWaiter设置共享标志。 Node() { } // 构造方法用于创建一个带有条件队列的节点 Node(Thread thread, Node mode) { this.nextWaiter = mode; this.thread = thread; } // 用于创建一个带有初始等waitStatus的节点 Node(Thread thread, int waitStatus) { this.waitStatus = waitStatus; this.thread = thread; } } |
CLH同步队列采用的是双向链表队列,头部节点默认获取资源获得执行权限。后续节点不断自旋方式查询前置节点是否执行完成,
直到头部节点执行完成将自己的waitStatus状态修改以通知后续节点可以获取资源执行。CLH锁是一个有序的无饥饿的公平锁。
Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备时 ,这些等待线程才会被唤醒,从而重新争夺锁
AbstractOwnableSynchronizer 抽象独占线程同步器: 用于记录时哪一个线程获取了锁
public abstract class AbstractOwnableSynchronizer implements Serializable { private static final long serialVersionUID = 3737899427754241961L; private transient Thread exclusiveOwnerThread; // transient用于修饰不需要序列化的字段,如果一个引用类型被transient修饰,则其反序列化的值为null,如果一个基本类型被transient修饰,则其反序列化的值为0 protected AbstractOwnableSynchronizer() { } // 设置独占线程 protected final void setExclusiveOwnerThread(Thread var1) { this.exclusiveOwnerThread = var1; } // 获取独占线程 protected final Thread getExclusiveOwnerThread() { return this.exclusiveOwnerThread; } } |