在上一篇博客并发编程--读写锁ReadWriteLock和ReentrantReadWriteLock(一)中我们简单介绍了一下读写锁的相关知识,接下来来我们介绍一下读锁的实现机制,简单的来说写锁就是一个独占锁,如果看过ReentrantLock相关的知识,应该会对独占锁的实现有一些简单的理解,简单来说独占锁的实现是当锁标识位state为0时,当前线程获取锁,并将state进行加一操作,其他线程来获取锁时会发现state此时不为0,则将当前线程添加到FIFO队列中,并将线程进行阻塞,线程释放锁时会将state进行减一操作,并唤醒FIFO队列中阻塞的线程来竞争锁。
接下来我们看看写锁是如何实现的。 lock的操作是如果获取锁则将锁标识位state进行加一操作。
public void lock() {
sync.acquire(1);//锁标识位加一
}
acquire中会尝试获取锁,如果获取锁失败则将线程添加到FIFO队列中挂起
public final void acquire(int arg) {
//tryAcquire(arg) 尝试获取锁,如果获取锁失败调用acquireQueued将线程添加FIFO队列中并挂起
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
在tryAcquire中判断是否可以获取锁,这里有一点不同的是如果一个读线程获取了锁,则锁标识位state加上65536
protected final boolean tryAcquire(int acquires) {
//获取当前线程
Thread current = Thread.currentThread();
//获取锁标识位值
int c = getState();
//与65535进行与运算,如果此时读线程获取锁则c=65536,此时w=0
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
//当此时有写锁或者读锁是都返回false
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
//获取写锁
setState(c + acquires);
return true;
}
//获取写锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
释放写锁:
public void unlock() {
sync.release(1);//将state值进行减一操作
}
release中的操作是释放锁并唤起其他阻塞的线程
public final boolean release(int arg) {
//释放锁
if (tryRelease(arg)) {
Node h = head;
//唤起其他阻塞线程
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//将state进行减release操作
int nextc = getState() - releases;
//当nextc等于0时将当前执行线程设置为null
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
//修改锁标识位
setState(nextc);
return free;
}
这样就完成了锁的释放。
接下来我们来看看读锁是如何获取锁的,并且读锁是共享锁,我们来了解一下读锁是如何实现共享锁的。
调用lock方法获取锁
public void lock() {
//获取锁
sync.acquireShared(1);
}
acquireShared会尝试获取锁,如果无法获取锁时则将当前线程添加到FIFO队列中,并将线程阻塞
public final void acquireShared(int arg) {
//尝试获取锁
if (tryAcquireShared(arg) < 0)
//将线程添加到FIFO队列中
doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
//获取锁标识位值
int c = getState();
//如果是不存在锁exclusiveCount(c)等于0,如果是存在读锁exclusiveCount(c)65536&65535等于0,如果存在写锁exclusiveCount(c)返回值不为0
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
//最终设置锁标识位位state=state+65536,这个是与写锁不同的地方
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 != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
读锁在设置锁时将state设置为state=state+65536,那样释放锁时state设置为state = state-65536,这样就完成了锁的释放操作。