参考文献:
www.cnblogs.com/leesf456/p/… www.cnblogs.com/fsmly/p/112… ifeve.com/introduce-a… www.blogjava.net/zhanglongsr… www.cnblogs.com/waterystone…
基本概念
公平锁:多个线程竞争锁时需要排队
非公平锁:多个线程竞争锁时,先尝试插队,插队失败再排队
公平锁与非公平锁的区别:公平锁在获取锁的时候,尝试获取锁失败,会加入队列尾部(阻塞队列),待释放锁之后,队列首部获取锁; 非公平锁是不需要排队,直接抢占式获取锁;
可重入锁:一个线程的多个流程可以获取同一把锁
非可重入锁:一个线程的多个流程不能获取同一把锁
共享锁:多个线程能共享一把锁
排它锁:多个线程不能共享一把锁
Synchronized是可重入锁:虚拟机 ObjectMonitor.hpp定义了synchronized怎么实现重入锁 count+1
ReentrantLock是基于AQS实现的可重入锁,默认创建非公平锁
AQS的理解
AQS定义了一套多线程访问共享资源的同步器框架,是一个依赖状态(state)的同步器;
AQS具备特征:
-
阻塞等待队列
-
共享/独占
-
公平/非公平
-
可重入
-
允许中断
Java.concurrent.util当中同步器的实现如Lock,Latch,Barrier等,都是基于AQS框架实现;
-
一般通过定义内部类Sync继承AQS
-
将同步器所有调用都映射到Sync对应的方法
AQS内部维护属性volatile int state (32位)
- state表示资源的可用状态
AQS中定义的二种资源方式
Exclusive -- 独占模式,只有一个线程能执行,例如ReentrantLock
Share -- 共享模式,多个线程可以同时执行,例如Semaphore、CountDownLatch
AQS中定义的二种队列
-- 同步等待队列
-- 条件等待队列
不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:
-- isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它
-- tryAcquire(int):(独占方式)尝试获取资源,成功则返回true,失败则返回false
-- tryRelease(int):(独占方式)尝试释放资源,成功则返回true,失败则返回false
-- tryAcquireShared(int):(共享方式)尝试获取资源 负数表示失败,0表示成功;但没有剩余可用资源;正数表示成功,且有剩余资源
-- tryReleaseShared(int):(共享方式)尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false
同步等待队列
条件等待队列
AQS源码理解
AQS类本身的属性
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
implements java.io.Serializable {
private static final long serialVersionUID = 7373984972572414691L;
//内部节点类,队列的实现(条件队列,等待队列)
static final class Node {
//省略Node属性...
}
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
//指向同步等待队列的头节点
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
//指向同步等待队列的尾节点
private transient volatile Node tail;
//The synchronization state. 线程可用状态
private volatile int state;
//state的三种访问方式
getState() 、setState()、compareAndSetState()
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//线程节点加入CLH同步队列
private Node addWaiter(Node mode) {
// 1. 将当前线程构建成Node类型
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
// 2. 1当前尾节点是否为null?
if (pred != null) {
// 2.2 将当前节点尾插入的方式
node.prev = pred;
// 2.3 CAS将节点插入同步队列的尾部
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//如果尾节点不为null
enq(node);
return node;
}
private Node enq(final Node node) {
//循环处理
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//队列为空需要初始化,创建空的头节点
if (compareAndSetHead(new Node()))
tail = head;
} else {
//当前线程的前置节点指向尾节点
node.prev = t;
//set尾部节点
if (compareAndSetTail(t, node)) {//当前节点置为尾部
t.next = node; //前驱节点的next指针指向当前节点
return t;
}
}
}
}
//判断当前同步队列中是否含有等待获取锁的线程节点
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
//其它省略。。。。
}
Node构成的CLH队列
//内部节点类,队列的实现(条件队列,等待队列)
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;
/**
* 标记当前节点的信号量状态 (1,0,-1,-2,-3)5种状态
* 使用CAS更改状态,volatile保证线程可见性,高并发场景下,
* 即被一个线程修改后,状态会立马让其他线程可见。
*/
volatile int waitStatus;
//前驱节点指针,当前节点加入到同步队列中被设置
volatile Node prev;
//后继节点指针
volatile Node next;
//记录当前节点的线程
volatile Thread thread;
/**
* 等待队列中的后继节点,如果当前节点是共享的,那么这个字段是一个SHARED常量,
* 也就是说节点类型(独占和共享)和等待队列中的后继节点共用同一个字段。
*/
Node nextWaiter;
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(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
ReentrantLock为什么可以实现加锁?
ReentrantLock内部维护了一个AQS对象,AQS对象中存在一个属性state,通过对象属性值的改变实现加锁与解锁;
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
private final Sync sync;
//内部类,继承AbstractQueuedSynchronizer(抽象队列同步器)
abstract static class Sync extends AbstractQueuedSynchronizer{
abstract void lock();
}
//内部类的子类 非公平锁类
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
}
//内部类的子类公平锁类
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
}
//本类的lock方法
public void lock() {
//调用内部类
sync.lock();
}
}
//TODO 利用多态,最终调用的是公平锁/非公平锁的lock方法
1、公平锁加锁的方式
在加锁的适合会先尝试获取锁,如果自定义的加锁类没有重写尝试加锁的方法就会去调用 AbstractQueuedSynchronizer的tryAcquire方法,
抛出异常throw new UnsupportedOperationException();
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
//TODO 调用AQS中acquire()
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//tryAcquire()尝试加锁,应该是模板方法设计模式,在父类中声明 子类实现
addWaiter()线程节点加入CLH队列
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//设置占用排它锁的线程是当前线程(独占模式)
setExclusiveOwnerThread(current);
return true;
}
}
//如果是当前线程已经获取到锁 state = state + 1
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
//尝试加锁的方式
-- 获取到线程的状态(默认初始化值为0)
-- hasQueuedPredecessors()判断线程是否需要排队,需要排队就返回true,反之则是false
-- compareAndSetState() 通常用于在获取到锁之前,尝试加锁时,对state进行修改,这种场景下,由于当前线程不是锁持有者,所以对state的修改是线程不安全的,也就是说可能存在多个线程都尝试修改state,所以需要保证对state修改的原子性操作,即使用了unsafe类的本地CAS方法;
-- setState方法通常用于当前正持有锁的线程对state变量进行修改,不存在竞争,是线程安全的,所以此处没必要用CAS保证原子性,修改的性能更重要
--(可重入锁)如果是当前线程已经获取到锁 state = state + 1
2、非公平锁加锁的方式
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
//多个线程竞争修改state 使用compareAndSetState()
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
最终调用的是父类Sync的nonfairTryAcquire()
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}