1. 写锁加锁流程
- 代码示例
- 写锁源码
结论:
- 加锁成功
1、锁没有被人持有
2、锁是重入 - 其他情况都会加锁失败
代码分析
protected final boolean tryAcquire(int acquires) {
//1. 获取当前线程
Thread current = Thread.currentThread();
//2. 获取当前锁的状态(默认是0,表示锁没有被人持有)
int c = getState();
//3. 获取写锁的状态(因为读写锁是锁的同一个对象,所以为了标识读写锁,把锁的前16位标识读锁的状态,后16位标识写锁的状态)
int w = exclusiveCount(c);
//4. 如果c!=0,表示有人上了锁(写锁或者读锁)
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
//5. 如果w==0表示从来没上过写锁,此时的锁只能是读锁,而当前自己是要上写锁,属于锁升级,进入if加锁失败,这就是为什么不允许锁升级的原因;
//如果进到第二个或判断 || ,表示w!=0,说明之前上过写锁,现在可能是写锁,也可能是读锁;此时判断是不是重入,如果不是重入,表示当前线程不是加了锁的线程,则current != getExclusiveOwnerThread(),也会加锁失败
//以上两种情况都会进入if,返回false,加锁失败
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//6. 如果w!=0,而且是重入,走到此行代码,判断w+1是不是大于最大重入次数65535(2的16次方减1)
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
//7. 没用超出重入的最大限制,则把w+1
setState(c + acquires);
return true;
}
//8. writerShouldBlock判断要不要排队,如果是非公平锁,writerShouldBlock()=false,compareAndSetState(c, c + acquires)进行抢锁,如果抢锁成功,因为前面有感叹号,则不会进入if,如果抢锁失败,则进入if
//如果是公平锁,则判断队列当中有没有人排队,如果有人排队,则writerShouldBlock()=true,进入if;如果没人排队,则抢锁compareAndSetState(c, c + acquires)
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//9. 加锁成功则把当前持有锁的线程设置为自己
setExclusiveOwnerThread(current);
return true;
}
2. 读锁加锁流程
- 读锁源码
结论:
- 加锁成功
1、没人上写锁
2、重入锁降级 - 其他情况都会加锁失败
代码分析
protected final int tryAcquireShared(int unused) {
//1. 获取当前线程
Thread current = Thread.currentThread();
//2. 获取当前锁的状态(默认是0,表示锁没有被人持有)
int c = getState();
//3. 如果exclusiveCount(c) != 0表示上了写锁
//4. 如果exclusiveCount(c) != 0,进入第二个判断,getExclusiveOwnerThread() != current表示不是重入
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//5. 如果上面的if没有进,可能有2种情况,1、没人上写锁,2、有人上写锁,而且是重入,有加了读锁,属于锁降级(先写锁后读锁)
int r = sharedCount(c);
//6. 判断需不需要排队,是不是小于最大的加锁次数,加锁有没有成功
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
//7. r==0表示这是第一次加读锁
if (r == 0) {
//8. 把当前线程赋给局部变量firstReader
firstReader = current;
//9. 记录当前线程的加锁次数
firstReaderHoldCount = 1;
//10. 如果是重入,则r==1,当前线程和上一个线程是同一个,会进入else if,firstReaderHoldCount变为2
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
//11. 如果第一个线程已经释放了锁,这时第二次线程来加读锁,则r=1,而且不是重入,就会进入else内
//先把一个为null的局部变量cachedHoldCounter赋给rh
HoldCounter rh = cachedHoldCounter;
//12. 第二个线程来加锁,则rh==null,进入if
//12.1 如果是第三个线程来加锁,rh!=null,因为第二个线程加锁的时候,给rh赋过值了,进入第二个判断,第二个线程的id肯定不等于当前线程的id
if (rh == null || rh.tid != getThreadId(current))
//13. get()方法看下面的代码分析,当第二个线程执行到这时,get方法返回的是一个new出来的HoldCounter对象,赋给rh和cachedHoldCounter,而且在ThreadLocalMap内把后面的线程都保存了
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
//14. HoldCounter对象内的count+1
rh.count++;
}
//15. 加锁成功,返回1
return 1;
}
return fullTryAcquireShared(current);
}
public T get() {
Thread t = Thread.currentThread();
//1. 从当前线程的ThreadLocal中得到map
ThreadLocalMap map = getMap(t);
//2. 第二线程进来,map内肯定是什么都没有存的,==nul,不会进if
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//3.
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//2. 第三个线程过来,map!=null(因为被第二个线程创建了),进入if,把线程信息保存到ThreadLocalMap中
if (map != null)
map.set(this, value);
//1. 第二个线程过来,map==null,执行到这里,创建ThreadLocalMap,将value存入
else
createMap(t, value);
return value;
}
public HoldCounter initialValue() {
return new HoldCounter();
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//HoldCounter 的数据结构,count计数,tid存当前线程id
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
- t1加读锁
- t2加读锁
- t3加读锁
- 上图可以看出,读锁加锁后,第一个线程id存在firstReader,加锁次数存在firstReaderHoldCount,都是ReentrantLock内的全局变量;
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
- 最后一个锁存在cachedHoldCounter,有加锁次数count和线程id,也是ReentrantLock内的全局变量;
private transient HoldCounter cachedHoldCounter;
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
- 使用全局变量来提高获取线程信息的效率
- 其余线程信息都放在ThreadLocalMap中