JAVA 并发编程 - ReadWriteLock 读写锁 时间: 2018-09-14 18:27 分类: 多线程,JAVA ####简介 `ReadWriteLock`是`JDK1.5`中提供的读写锁。之所以叫读写锁,是因为它采用锁分离的机制来有效地帮助减少锁竞争。比如有 R1、R2、R3 三个读线程,W1、W2、W3 三个写线程,如果用`ReentrantLock`或者`synchronized`来实现读写同步的话,那么无论是`读读操作`、`读写操作`还是`写写操作`都是串行的,但是,很明显的是`读读操作`并不会破坏数据的一致性。所以如果换成`ReadWriteLock`读写锁的话,`读读操作`将会变成并行的,而其它两个操作依旧是串行的,所以在对于`读多写少`的应用场景可以明显的提升系统性能,下面是读写锁的访问性约束: | \ | 读 | 写 | | -- | -- | -- | |读|非阻塞|阻塞| |写|阻塞|阻塞| * 读-读不互斥:读读之间不会进行锁等待 * 读-写互斥:读写之间会进行锁等待 * 写-写互斥:写写之间会进行锁等待 ####实践 下面是一个`读多写少`的测试程序: ```java import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockTest { //重入锁 private static ReentrantLock lock = new ReentrantLock(); //读写锁 private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //读锁 private static Lock readLock = readWriteLock.readLock(); //写锁 private static Lock writeLock = readWriteLock.writeLock(); private int value; public int read(Lock lock) throws InterruptedException { try { lock.lock(); Thread.sleep(1000); //模拟耗时操作 return value; } finally { lock.unlock(); } } public void write(Lock lock, int value) throws InterruptedException { try { lock.lock(); Thread.sleep(1000); //模拟耗时操作 this.value = value; } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { long startTime = System.currentTimeMillis(); CountDownLatch cdl = new CountDownLatch(21); ReadWriteLockTest test = new ReadWriteLockTest(); //20个读线程 for (int i = 0; i < 20; i++) { Thread t = new Thread(() -> { try { test.read(lock); //使用重入锁 //test.read(readLock); //使用读写锁 } catch (InterruptedException e) { e.printStackTrace(); } finally { cdl.countDown(); } }); t.start(); } //1个写线程 Thread t = new Thread(() -> { try { test.write(lock, new Random().nextInt()); //使用重入锁 //test.write(writeLock, new Random().nextInt()); //使用读写锁 } catch (InterruptedException e) { e.printStackTrace(); } finally { cdl.countDown(); } }); t.start(); cdl.await(); System.out.println("总用时:" + (System.currentTimeMillis() - startTime) + "ms"); } } ``` 经过测试可以发现,使用重入锁的时候上面代码至少需要 21s 以上才能执行完毕,而换成重入锁执行只要 2s 左右,原因如下: * 使用重入锁的时候,由于所有的读写操作之间都是串行的,所以 21 个线程必须要 21s 以上的时间才能执行完毕。 * 使用读写锁的时候,由于读读操作之间是不需要锁等待的,所以 20 个读线程可以是并行的,再与 1 个写线程串行,也就是至少 2s 的时间就能执行完毕。 标签: ReadWriteLock JAVA 并发编程