最近研究了一下java中的锁,在看读写锁的代码时,发现很难理解javadoc中关于这个类的解释。于是自己对着代码理解了一下,在此记录,有些地方看得不是太明白,如有错误请纠正。
从不同的视角看锁,锁具有如下特性:
公平性:
公平锁和非公平锁:如果获取锁的顺序是按照请求锁的顺序进行的,那么就是公平锁,否则就是非公平锁
互斥性:
独占锁和共享锁:独占锁只能由一个线程持有,如果独占锁是可重入锁(同一线程可以获取多次),则同一线
程可以获取多次。
共享锁可由多个线程获取。
ReentrantReadWriteLock可以提高锁的并发性,读读共享,读写、写写互斥。非公平锁会优先为等待时间最
长的那个写线程分配写入锁(由非公平读锁的实现决定)。
读写锁的公平性:
非公平读锁:非公平读获取读锁时,判断队列头结点是不是独占结点,是则自旋等待。
非公平写锁:非公平写获取锁时,先尝试直接获取写锁。
公平读锁:不是等待队列的头结点时,不能获取读锁。
公平写锁:不是等待队列的头结点时,不能获取写锁。
锁降级:
锁降级获取锁的顺序:
writelock.lock();
readlock.lock();
writelock.unlock();
readlock.unlock();
锁降级获取写锁时,不能有线程再获取读锁,当修改完数据时,当前线程获取读锁后,可继续执行操作。
不会被其它写线程影响,因为此时其它写线程获取不到锁。
锁升级:
锁升级是不允许的,这里看源代码的时候比较困惑,代码里实际上是可以获取取锁的。不过,这里存在
一个死锁条件,锁升级获取锁的顺序如下:
readlock.lock();
writelock.lock();
readlock.unlock();
writelock.unlock();
为什么会死锁?
因为要想获取写锁,要释放所有读锁,当多个线程同时获取到读锁再获取写锁时,会在获取写锁处等待读
锁释放,导致死锁。
//读写锁用高16位表示共享锁,低16位表示独占锁
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//可表示锁的最大值
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//计算共享锁数量
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//计算独占锁数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
/**
获取独占锁
**/
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
if ((w == 0 && writerShouldBlock(current)) ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
1.如c不等于0,w等于0,此时线程持有共享锁,不能获取独占锁
2.如c不等于0,获取锁的数量大于最大锁数量,抛异常
3.如果w等于0(其它线程未持有独占锁),这时看写是不是应该被阻塞
4.如果更新锁状态成功,返回true
5.如果c等于0,返回true
/**
获取共享锁
**/
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (!readerShouldBlock(current) &&
compareAndSetState(c, c + SHARED_UNIT)) {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
rh.count++;
return 1;
}
return fullTryAcquireShared(current);
}
1.如果写锁由其它线程持有,获取失败
2.如果共享锁是最大值,获取失败
3.判断读锁是否该阻塞,公平锁判断该线程是否为头结点,是头结点获取成功,不是则阻塞
非公平锁判断头结点的下一等待结点是否在共享模式下等待,是则成功,不是则阻塞