验证读锁和读锁不互斥:
public class ReentranReadWirteLockMain1 {
public static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
testReadFun();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Stream.of("thread01", "thread02", "thread03").forEach(s -> new Thread(runnable, s).start());
}
public static void testReadFun() throws InterruptedException {
reentrantReadWriteLock.readLock().lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " get read lock");
Thread.sleep(5000);
System.out.println("current thread name:" + Thread.currentThread().getName() + " read lock end");
} finally {
reentrantReadWriteLock.readLock().unlock();
}
}
}
运行结果:
多个线程不会等待其他线程释放锁,先输出前三行,之后5秒之后输出后三行。说明启动的时候都可以获得读锁,不会等待。提升读取的效率,不会出现线程等待问题。
验证一下一个线程读操作和另外一个写操作会不会互斥
public class ReentranReadWirteLockMain3 {
public static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
testReadFun();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
try {
testWriteFun();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable, "thread01");
thread.start();
Thread.sleep(1000);
Thread thread2 = new Thread(runnable2, "thread02");
thread2.start();
}
public static void testReadFun() throws InterruptedException {
reentrantReadWriteLock.readLock().lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " get read lock");
System.out.println(System.currentTimeMillis());
Thread.sleep(5000);
} finally {
reentrantReadWriteLock.readLock().unlock();
}
}
public static void testWriteFun() throws InterruptedException {
reentrantReadWriteLock.writeLock().lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " get write lock");
System.out.println(System.currentTimeMillis());
Thread.sleep(5000);
} finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
}
运行结果:
我们看看结果,时间相隔5秒,这是怎么回事?这个是读锁与写锁互斥导致的,不会同时又进入写操作和读操作。
验证一下一个线程写操作和另外一个写操作会不会互斥
public class ReentranReadWirteLockMain2 {
public static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
testReadFun();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Stream.of("thread01", "thread02", "thread03").forEach(s -> new Thread(runnable, s).start());
}
public static void testReadFun() throws InterruptedException {
reentrantReadWriteLock.writeLock().lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " get write lock");
Thread.sleep(5000);
System.out.println("current thread name:" + Thread.currentThread().getName() + " write lock end");
} finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
}
运行结果:
有运行结果可以看出,打印的时候最开始那个线程先获取锁就等待5秒输出current thread name: 线程名 read lock end,之后再运行获取锁的操作,有打印可以看出写操作是互斥操作
上面读锁和写锁互斥,其实反过来也是一样的。
Thread thread2 = new Thread(runnable2, "thread02");
thread2.start();
Thread.sleep(1000);
Thread thread = new Thread(runnable, "thread01");
thread.start();
运行结果:
证明写锁和读锁也是互斥的。
我们来看看读操作和写操作互斥原理:
源码:
ReentrantReadWriteLock也会有公平锁和非公平锁之分,和ReentrantLock一致,但是就是会有读锁和写锁的处理。
//一个读锁
private final ReentrantReadWriteLock.ReadLock readerLock;
//一个写锁
private final ReentrantReadWriteLock.WriteLock writerLock;
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
读锁实现核心代码:
//以共享模式获取,忽略中断。通过首先至少调用一次tryAcquireShared并成功返回来实现。
//否则,线程将排队,并可能反复阻塞和解除阻塞,并调用tryAcquireShared直到成功。
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
tryAcquireShared()方法:
//如果另一个线程持有写锁定,则失败。 否则,此线程有资格进入锁定wrt状态,
//因此请问是否由于队列策略而应阻塞。如果不是,请尝试按CASing状态授予许可并更新计数。
//请注意,此步骤不会检查重入获取,这会推迟到完整版本,以避免在更典型的非重入情况下必须检查保留计数。
//如果第2步失败,或者由于线程显然不符合条件或者CAS失败或计数饱和,请使用完全重试循环链接到版本。
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
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 != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
fullTryAcquireShared()方法:
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
//否则我们将持有排他锁;在此处阻塞将导致死锁。
} else if (readerShouldBlock()) {
// 确保我们没有重新获取读锁
if (firstReader == current) {
// 断言firstReaderHoldCount> 0
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
写锁实现核心代码:
//在独占模式下获取,忽略中断。通过至少调用一次tryAcquire并成功返回来实现
//否则,线程将排队,并可能反复阻塞和解除阻塞,并调用tryAcquire直到成功
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
//如果读取计数为非零或写入计数为非零,并且所有者是另一个线程,则失败。
//如果计数将饱和,则失败。 (只有在count已经不为零时,才可能发生这种情况。)
//否则,如果该线程是可重入获取或队列策略允许的话,则有资格进行锁定。如果是这样,请更新状态并设置所有者。
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");
// Reentrant acquire
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}