ReadWriteLock是JDK5中提供的读写分离锁.读写锁可以有效的减少锁竞争,提升系统的吞吐量。

读写锁的访问约束情况

非阻塞 阻塞
阻塞 阻塞

读读之间不阻塞,读和写之间相互阻塞,写和写之间阻塞。系统中如果读操作比写操作多,则可以考虑使用读写锁增加系统的吞吐量,提高系统的性能。
读写锁允许多个读线程同时读,使得多个读线程真正并行。但是由于数据的完整性,所以读和写之间,写和写之间需要相互等待和持有锁。读操作占用的时间越长,则使用读写锁的优势越明显。

样例比较读写锁和重入锁的性能差距

测试电脑 配置
2.7 GHz 双核Intel Core i5, RAM 8 GB 1867 MHz DDR3
openjdk 16.0.1 OpenJDK 64-Bit Server VM Homebrew (build 16.0.1+0, mixed mode, sharing)


public class ReadWriteLockDemo {

    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

    private static Lock readLock = reentrantReadWriteLock.readLock();

    private static Lock writeLock = reentrantReadWriteLock.writeLock();

    private static ReentrantLock reentrantLock = new ReentrantLock();

    private static CountDownLatch countDownLatch = new CountDownLatch(20);  //多线程控制

    private static int readThreadNum = 20;
    private static int writeThreadNum = 2;

    private static CountDownLatch reentrantDownLatch = new CountDownLatch(20);

    private int value;

    public Object readSomething(Lock lock) throws InterruptedException {
        try {
            lock.lock();
            Thread.sleep(1000); //模拟耗时操作
            return value;
        } finally {
            lock.unlock();
        }
    }

    public Object writeSomething(Lock lock, int newValue) throws InterruptedException {
        try {
            lock.lock();
            Thread.sleep(1000); //模拟耗时操作
            this.value = newValue;
            return this.value;
        } finally {
            lock.unlock();
        }
    }


    public static void main(String[] args) throws InterruptedException {
        ReadWriteLockDemo demo = new ReadWriteLockDemo();

        Runnable readTask = () -> {
            try {
                demo.readSomething(readLock);
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Runnable writeTask = () -> {
            try {
                demo.writeSomething(writeLock, new Random().nextInt());
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Runnable readTask2 = () -> {
            try {
                demo.readSomething(reentrantLock);
                reentrantDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Runnable writeTask2 = () -> {
            try {
                demo.writeSomething(reentrantLock, new Random().nextInt());
                reentrantDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };



        long begin = System.currentTimeMillis();

        for (int i = 0; i < readThreadNum; i++) new Thread(readTask).start();

        for (int i = 0; i < writeThreadNum; i++) new Thread(writeTask).start();

        countDownLatch.await(); //等待子线程全部完成工作

        long end = System.currentTimeMillis();

        System.out.println("[readWriteLock cost] " + (end - begin) + "ms");



        long begin2 = System.currentTimeMillis();
        for (int i = 0; i < readThreadNum; i++) new Thread(readTask2).start();

        for (int i = 0; i < writeThreadNum; i++) new Thread(writeTask2).start();

        reentrantDownLatch.await(); //等待子线程全部完成工作

        long end2 = System.currentTimeMillis();

        System.out.println("[reentrantLock cost] " + (end2 - begin2) + "ms");
    }

}

输出值

两者的输出值,在笔者电脑上为如下所示:

[readWriteLock cost] 1011ms
[reentrantLock cost] 20069ms

总结

性能相差20倍多,不同的电脑和jdk版本表现有所不同。如果将读线程的数量或者耗时增加,则读写锁的性能优势会更加明显。在读多的场景下可以采用读写锁提升性能。