1 AQS
管程
- 管程:让管理共享变量以及操作共享变量支持并发
- 互斥:同一时刻只支持一个线程进行操作
- 同步:线程之间的协作以及通信
MESA模型
入口等待队列
多个线程试图进入排队,线程CAS获取锁成功进入入口,其他线程进入入口线程等待实现了线程之间的互斥
共享变量
线程进入管程后可以操作共享变量
条件变量&条件队列
当线程满足对应的条件时,将自己阻塞然后放入条件等待队列,或者从条件队列重新放入等待队列
java针对管程有2种实现
- 基于object的Monitor机制,用于synchronized内置锁
- 抽象同步器AQS,用于juc下的Lock锁机制
如何实现一把独占锁
state:是否加锁 0 1 使用cas
等待队列:加锁失败的线程数据,数组or链表
等待唤醒某个线程:
LockSupport.park/unpark
共性逻辑 抽象类
入口等待队列和条件等待队列的出队入队
修改cas操作
等待唤醒机制:
synchronized + object.wait/notify/notifyAll
ReentrantLock + condition.await/signal/signalAll
底层通过操作系统的pthread的api
pthread_create
pthread_cond_wait
pthread_cond_signal
1.1 AQS是什么
java实现的抽象同步框架,可以实现一个依赖状态的同步器
public class MyLock extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
int state = getState();
if (state == 0){
if (compareAndSetState(0, arg)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
}
else if (Thread.currentThread() == getExclusiveOwnerThread()){
setState(state + arg);
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
int state = getState();
if (state > arg){
setState(state - arg);
}else {
setExclusiveOwnerThread(null);
setState(0);
}
return true;
}
}
1 ReentrantLock
公平锁&非公平锁
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
//没有获取到锁,添加到入口等待队列,独占模式
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//非公平锁
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;
}
//公平锁
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;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
1.1 加锁源码解读
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
//获取锁失败,新增一个Node入队
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
//新增的node指向老的tail
node.setPrevRelaxed(oldTail);
//cas操作将node赋值给tail,node最为新的尾节点
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
//初始化双向链表,注意这里的head节点的成员变量为null
initializeSyncQueue();
}
}
}
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
//cas循环操作,设置成功跳出逻辑
for (;;) {
//获取当前线程Node的prev
final Node p = node.predecessor();
//如果prev是head,再次尝试获取一次锁,获取成功就不会阻塞
//为了减小开销,不直接阻塞
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
// 设置prev节点的waitStatus = -1.以便后续唤醒
if (shouldParkAfterFailedAcquire(p, node))
//park 线程进入阻塞,等待唤醒,唤醒后还会继续循环获取锁
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
2 ReentrantReadWriteLock
如何设计一把读写锁?
读读共享,写读,写写互斥
互斥: tryAcquire, tryRelease 写锁: state 0 1
共享: tryAcquireShared, tryReleaseShared 读锁 state != 0两个状态: 读状态,写状态 int state
一个变量控制2种状态? 高低位
00000000 00000000 00000000 00000000 高16位,低16位读写互斥,加写锁怎么判断是否有读锁,如何实现可重入?
写锁:独占 只会有一个线程占用写锁 +1
读锁:运行多个线程重入 ThreadLocal
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing
*/
Lock writeLock();
}
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
Thread current = Thread.currentThread();
int c = getState();
//获取写锁,判断是否存在写锁;通过移位操作获取低16位
if (exclusiveCount(c) != 0 &&
//锁降级,如果有写锁,线程是自己,就继续获取读锁,否则获取失败
getExclusiveOwnerThread() != current)
return -1;
//获取读锁count
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null ||
rh.tid != LockSupport.getThreadId(current))
//通过threadLocal保存读锁的重入次数
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
** 锁降级**
获取写锁后再获取读锁是可以的
2.1 StampLock
乐观读
1.通过乐观读模式读取数据,读操作校验是否有写锁进入,如果有,就加读锁,没有就不加锁
缺陷
没有实现可重入,公平非公平,没有实现AQS
3 JUC并发容器
3.1 ConcurrentHashMap
jdk1.7
Segment<K,V> 数组 + HashEntry<K,V>数组 + 链表
segment继承了ReentrantLock类,每次进行写操作时,会对segment对象加锁,保证只有一个线程进行写操作;但是如果此时有多个线程读,我们如何保证线程能够读到写的线程修改后的值;当然是通过对我们的对象以及成员变量加上volatile关键字,被volatile关键字修饰的变量适用于单线程写,多线程读场景,所以在ConcurrentHashMap中就利用了该特性实现了读操作不用加锁
jdk1.8
在1.8版本,ConcurrentHashMap取消了分段锁,采用CAS加上synchronized关键字去实现变并发写逻辑,当对象的key的hash值对应到table[i]为null时,通过cas操作将Node<K,V>的内存地址更新到数组里边,如果失败,第2次时,发现数组有值,则对该链表加锁,再通过equals方法判断key是否相等,相等就把value赋值给Node的value变量;不相等继续遍历链表,直到链表中lastNode的next为空,将该Node赋值给lastNode的next属性
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
//获取hashCode,并通过移位操作将hashCode的高位参与运算得到新的hash值作为该Node的hash
int hash = spread(key.hashCode());
int binCount = 0;
//一直循环直到Node对象插入成功
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
//初始化Node数组
tab = initTable();
//table[i] = null CAS操作成功跳出循环,失败进入下一次循环重新判断条件
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
//这里进行扩容操作
tab = helpTransfer(tab, f);
else if (onlyIfAbsent // check first node without acquiring lock
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key.equals(fk)))
&& (fv = f.val) != null)
return fv;
else {
// tab[i]不为空,对链表加锁
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
// equals相等,更新value
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
//链表尾部插入Node
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key, value);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
else if (f instanceof ReservationNode)
throw new IllegalStateException("Recursive update");
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}