当读操作远远高于写操作时,这时候使用 读写锁 让 读-读
可以并发,提高性能。 类似于数据库中的 select ... from ... lock in share mode
提供一个数据容器类DataContainer内部分别使用读锁保护数据的 read() 方法,写锁保护数据的 write() 方法
需要注意的是 :
写-写
和 读-写
以及 写-读
都是互相阻塞的提供一个 数据容器类 内部分别使用读锁保护数据的 read() 方法,写锁保护数据的 write() 方法
package cn.knightzz.juc.reentrantlock.readwrite; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @author 王天赐 * @title: DataContainer * @projectName hm-juc-codes * @description: * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a> * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a> * @create: 2022-09-19 15:23 */ @SuppressWarnings("all") @Slf4j(topic = "c.DataContainer") public class DataContainer { private Object data; private ReentrantReadWriteLock rw = new ReentrantReadWriteLock(); private ReentrantReadWriteLock.ReadLock readLock = rw.readLock(); private ReentrantReadWriteLock.WriteLock writeLock = rw.writeLock(); public Object read() { // 开启读锁 log.debug("获取读取锁 ... "); readLock.lock(); try { log.debug("开始读取 ... "); TimeUnit.SECONDS.sleep(1); return data; } catch (InterruptedException e) { throw new RuntimeException(e); } finally { log.debug("释放读锁 ... "); readLock.unlock(); } } public void write() { log.debug("获取写入锁.."); writeLock.lock(); try { log.debug("开始写入数据 ... "); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { log.debug("释放写入锁 ... "); writeLock.unlock(); } } }
package cn.knightzz.juc.reentrantlock.readwrite; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.io.IOException; /** * @author 王天赐 * @title: ReadWriteLockTest * @projectName hm-juc-codes * @description: * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a> * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a> * @create: 2022-09-19 14:37 */ @SuppressWarnings("all") @Slf4j(topic = "c.ReadWriteLockTest") public class ReadWriteLockTest { public static void main(String[] args) throws IOException, InterruptedException { log.debug("==========================={}=========================", "测试读锁并发"); // 只有读锁是可以多个线程同时执行的 DataContainer container = new DataContainer(); for (int i = 1; i < 5; i++) { new Thread(() -> { container.read(); }, "t" + i).start(); } System.in.read(); log.debug("==========================={}=========================", "测试写锁并发"); // 写入 和 读写都是相互阻塞的, 只有一方释放锁, 另一方才可以获取对应的锁去执行 for (int i = 1; i < 5; i++) { new Thread(() -> { container.write(); }, "t" + i).start(); } System.in.read(); log.debug("==========================={}=========================", "测试读写锁相互阻塞"); new Thread(() -> { container.read(); }, "t1").start(); // sleep 100ms Thread.sleep(100); new Thread(() -> { container.write(); }, "t2").start(); System.in.read(); } }
读并发测试 :
19:16:10.757 [main] DEBUG c.ReadWriteLockTest - ===========================测试读锁并发========================= 19:16:10.791 [t1] DEBUG c.DataContainer - 获取读取锁 ... 19:16:10.792 [t2] DEBUG c.DataContainer - 获取读取锁 ... 19:16:10.792 [t3] DEBUG c.DataContainer - 获取读取锁 ... 19:16:10.792 [t3] DEBUG c.DataContainer - 开始读取 ... 19:16:10.792 [t4] DEBUG c.DataContainer - 获取读取锁 ... 19:16:10.792 [t1] DEBUG c.DataContainer - 开始读取 ... 19:16:10.792 [t2] DEBUG c.DataContainer - 开始读取 ... 19:16:10.792 [t4] DEBUG c.DataContainer - 开始读取 ... 19:16:11.796 [t1] DEBUG c.DataContainer - 释放读锁 ... 19:16:11.796 [t2] DEBUG c.DataContainer - 释放读锁 ... 19:16:11.796 [t4] DEBUG c.DataContainer - 释放读锁 ... 19:16:11.796 [t3] DEBUG c.DataContainer - 释放读锁 ...
可以看到上面执行结果 : 开始读取的时间几乎是同一时间, 可以说明他们是同时获取锁的,
写并发测试 :
19:16:15.580 [main] DEBUG c.ReadWriteLockTest - ===========================测试写锁并发========================= 19:16:15.581 [t1] DEBUG c.DataContainer - 获取写入锁.. 19:16:15.581 [t2] DEBUG c.DataContainer - 获取写入锁.. 19:16:15.581 [t1] DEBUG c.DataContainer - 开始写入数据 ... 19:16:15.581 [t3] DEBUG c.DataContainer - 获取写入锁.. 19:16:15.581 [t4] DEBUG c.DataContainer - 获取写入锁.. 19:16:16.587 [t1] DEBUG c.DataContainer - 释放写入锁 ... 19:16:16.587 [t2] DEBUG c.DataContainer - 开始写入数据 ... 19:16:17.602 [t2] DEBUG c.DataContainer - 释放写入锁 ... 19:16:17.602 [t3] DEBUG c.DataContainer - 开始写入数据 ... 19:16:18.606 [t3] DEBUG c.DataContainer - 释放写入锁 ... 19:16:18.606 [t4] DEBUG c.DataContainer - 开始写入数据 ... 19:16:19.610 [t4] DEBUG c.DataContainer - 释放写入锁 ...
可以看到 t1 获取锁后开始写入数据, 此时有其他线程都在尝试获取锁, 但是都没有成功, 在t1释放锁后, t3线程获取到锁开始写入数据
19:16:21.075 [main] DEBUG c.ReadWriteLockTest - ===========================测试读写锁相互阻塞========================= 19:16:21.076 [t1] DEBUG c.DataContainer - 获取读取锁 ... 19:16:21.076 [t1] DEBUG c.DataContainer - 开始读取 ... 19:16:21.185 [t2] DEBUG c.DataContainer - 获取写入锁.. 19:16:22.085 [t1] DEBUG c.DataContainer - 释放读锁 ... 19:16:22.085 [t2] DEBUG c.DataContainer - 开始写入数据 ... 19:16:23.091 [t2] DEBUG c.DataContainer - 释放写入锁 ...
可以看到上面的运行结果, t1线程先获取读锁, 此时t2线程也尝试获取写入锁, 但是等到1s后t1读取完成并释放锁后, t2线程才开始写入数据.
使用读写锁保证缓存一致性
本文作者:王天赐
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!