开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 28 天,点击查看活动详情
大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实,最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈
前言
JUC并发包中提供了ReentrantReadWriteLock作为读写锁,本篇文章将对读写锁的如下四个场景进行演示。
- 当前线程获取
读锁时,读锁是否被获取不会影响读锁的获取; - 当前线程获取
读锁时,若写锁未被获取或者写锁被当前线程获取,则允许获取读锁,否则进入等待状态; - 当前线程获取
写锁时,若读锁已经被获取,无论获取读锁的线程是否是当前线程,都进入等待状态; - 当前线程获取
写锁时,若写锁已经被其它线程获取,则进入等待状态。
正文
一. 场景一
本小节演示:当前线程获取读锁时,读锁是否被获取不会影响读锁的获取。示例如下。
public class ReentrantReadWriteLockTest {
@Test
public void 当前线程获取读锁_读锁是否被获取不会影响读锁的获取() throws Exception {
// 创建读写锁
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
Lock readLock = reentrantReadWriteLock.readLock();
// 创建CountDownLatch对象
CountDownLatch countDownLatch = new CountDownLatch(2);
// 创建任务
Runnable readLockRunnable1 = new Runnable() {
@Override
public void run() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取读锁");
Thread.sleep(1000);
} catch (InterruptedException e) {
// ignore
} finally {
readLock.unlock();
countDownLatch.countDown();
}
}
};
Runnable readLockRunnable2 = new Runnable() {
@Override
public void run() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取读锁");
Thread.sleep(1000);
} catch (InterruptedException e) {
// ignore
} finally {
readLock.unlock();
countDownLatch.countDown();
}
}
};
// 线程1先获取读锁
Thread thread1 = new Thread(readLockRunnable1, "线程1");
thread1.start();
// 线程2再获取读锁
Thread thread2 = new Thread(readLockRunnable2, "线程2");
thread2.start();
// 阻塞主线程
countDownLatch.await();
}
}
上述示例中,演示了线程1和线程2先后获取读锁,如果获取到读锁,则打印一条日志。运行测试程序,结果如下。

运行结果表明,当前线程获取读锁时,读锁是否被获取不会影响读锁的获取。
二. 场景二
本小节演示:当前线程获取读锁时,若写锁未被获取或者写锁被当前线程获取,则允许获取读锁,否则进入等待状态。示例如下。
public class ReentrantReadWriteLockTest {
@Test
public void 当前线程获取读锁_若写锁已经被当前线程获取则当前线程能获取到读锁() throws Exception {
// 创建读写锁
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
Lock readLock = reentrantReadWriteLock.readLock();
Lock writeLock = reentrantReadWriteLock.writeLock();
// 创建CountDownLatch对象
CountDownLatch countDownLatch = new CountDownLatch(1);
// 创建任务
Runnable runnable = new Runnable() {
@Override
public void run() {
// 先拿写锁
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取到了写锁");
// 同一线程中再拿读锁
if (readLock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + " 获取到了读锁");
} finally {
readLock.unlock();
}
}
} finally {
writeLock.unlock();
countDownLatch.countDown();
}
}
};
// 创建线程
Thread thread = new Thread(runnable, "线程1");
thread.start();
// 阻塞主线程
countDownLatch.await();
}
}
上述示例中,演示了同一线程先后获取写锁和读锁,获取到对应锁后,打印日志。运行测试程序,结果如下所示。

运行结果表明,当前线程获取读锁时,若写锁被当前线程获取,则允许获取读锁。
再看如下示例。
public class ReentrantReadWriteLockTest {
@Test
public void 当前线程获取读锁_若写锁已经被其它线程获取则当前线程无法获取读锁() throws Exception {
// 创建读写锁
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
Lock readLock = reentrantReadWriteLock.readLock();
Lock writeLock = reentrantReadWriteLock.writeLock();
// 创建CountDownLatch对象
CountDownLatch countDownLatch = new CountDownLatch(2);
// 创建拿写锁的任务
Runnable writeLockRunnable = new Runnable() {
@Override
public void run() {
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取到了写锁");
Thread.sleep(1000);
} catch (InterruptedException e) {
// ignore
} finally {
writeLock.unlock();
countDownLatch.countDown();
}
}
};
// 创建拿读锁的任务
Runnable readLockRunnable = new Runnable() {
@Override
public void run() {
if (readLock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + " 获取到了读锁");
} finally {
readLock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + " 获取读锁失败");
}
countDownLatch.countDown();
}
};
// 创建线程拿写锁
Thread thread1 = new Thread(writeLockRunnable, "线程1");
thread1.start();
// 创建线程拿读锁
Thread thread2 = new Thread(readLockRunnable, "线程2");
thread2.start();
countDownLatch.await();
}
}
上述示例中,演示了线程1先获取写锁,线程2后获取读锁,并打印获取结果。运行测试程序,结果如下。

运行结果表明:当前线程获取读锁时,若写锁已被其它线程获取,则获取失败。
三. 场景三
本小节演示:当前线程获取写锁时,若读锁已经被获取,无论获取读锁的线程是否是当前线程,都进入等待状态。示例如下。
public class ReentrantReadWriteLockTest {
@Test
public void 当前线程获取写锁_若读锁已被当前线程获得则当前线程无法获取写锁() {
// 创建读写锁
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
Lock readLock = reentrantReadWriteLock.readLock();
Lock writeLock = reentrantReadWriteLock.writeLock();
// 创建CountDownLatch对象
CountDownLatch countDownLatch = new CountDownLatch(1);
// 创建任务
Runnable runnable = new Runnable() {
@Override
public void run() {
// 先拿读锁
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取到了读锁");
// 同一线程中再拿写锁
if (writeLock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + " 获取到了写锁");
} finally {
writeLock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + " 获取写锁失败");
}
} finally {
readLock.unlock();
countDownLatch.countDown();
}
}
};
// 创建线程并执行任务
Thread thread = new Thread(runnable, "线程1");
thread.start();
// 阻塞主线程
countDownLatch.countDown();
}
}
上述示例中,演示了同一线程先获取读锁,再获取写锁,并打印获取结果。运行测试程序,结果如下。

运行结果表明:当前线程获取写锁时,若读锁已经被获取,无论获取读锁的线程是否是当前线程,都获取失败。
四. 场景四
本小节演示:当前线程获取写锁时,若写锁已经被其它线程获取,则进入等待状态。示例如下。
public class ReentrantReadWriteLockTest {
@Test
public void 当前线程获取写锁_若写锁已经被其它线程获取则获取失败() throws Exception {
// 创建读写锁
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
Lock writeLock = reentrantReadWriteLock.writeLock();
// 创建CountDownLatch对象
CountDownLatch countDownLatch = new CountDownLatch(2);
// 创建拿写锁的任务
Runnable writeLockRunnable = new Runnable() {
@Override
public void run() {
if (writeLock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + " 获取到了写锁");
Thread.sleep(1000);
} catch (InterruptedException e) {
// ignore
} finally {
writeLock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + " 获取写锁失败");
}
countDownLatch.countDown();
}
};
// 创建线程1,拿写锁
Thread thread1 = new Thread(writeLockRunnable, "线程1");
thread1.start();
// 创建线程2,拿写锁
Thread thread2 = new Thread(writeLockRunnable, "线程2");
thread2.start();
// 阻塞主线程
countDownLatch.await();
}
}
上述示例中,演示了线程1先获取写锁,线程2后获取写锁,并打印获取结果。运行测试程序,结果如下。

上述结果表明:当前线程获取写锁时,若写锁已经被其它线程获取,则获取失败。
总结
读写锁ReentrantReadWriteLock的使用流程通常如下。
- 创建
ReentrantReadWriteLock对象; - 通过
ReentrantReadWriteLock的readLock()方法创建读锁ReentrantReadWriteLock.WriteLock; - 通过
ReentrantReadWriteLock的writeLock()方法创建写锁ReentrantReadWriteLock.ReadLock; - 后续加
读锁通过ReentrantReadWriteLock.WriteLock,加写锁通过ReentrantReadWriteLock.ReadLock。
读写锁的使用规则总结如下。
- 当前线程获取
读锁时,读锁是否被获取不会影响读锁的获取; - 当前线程获取
读锁时,若写锁未被获取或者写锁被当前线程获取,则允许获取读锁,否则进入等待状态; - 当前线程获取
写锁时,若读锁已经被获取,无论获取读锁的线程是否是当前线程,都进入等待状态; - 当前线程获取
写锁时,若写锁已经被其它线程获取,则进入等待状态。
读锁和写锁加锁时有两种方式。
- 通过
lock()方法加锁,失败则进入同步队列等待; - 通过
tryLock()方法加锁,成功返回true,失败返回false。
大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实,最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 28 天,点击查看活动详情