admin

JAVA 并发编程 - ReadWriteLock 读写锁
简介ReadWriteLock是JDK1.5中提供的读写锁。之所以叫读写锁,是因为它采用锁分离的机制来有效地帮助减...
扫描右侧二维码阅读全文
14
2018/09

JAVA 并发编程 - ReadWriteLock 读写锁

简介

ReadWriteLockJDK1.5中提供的读写锁。之所以叫读写锁,是因为它采用锁分离的机制来有效地帮助减少锁竞争。比如有 R1、R2、R3 三个读线程,W1、W2、W3 三个写线程,如果用ReentrantLock或者synchronized来实现读写同步的话,那么无论是读读操作读写操作还是写写操作都是串行的,但是,很明显的是读读操作并不会破坏数据的一致性。所以如果换成ReadWriteLock读写锁的话,读读操作将会变成并行的,而其它两个操作依旧是串行的,所以在对于读多写少的应用场景可以明显的提升系统性能,下面是读写锁的访问性约束:

\
非阻塞阻塞
阻塞阻塞
  • 读-读不互斥:读读之间不会进行锁等待
  • 读-写互斥:读写之间会进行锁等待
  • 写-写互斥:写写之间会进行锁等待

实践

下面是一个读多写少的测试程序:

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 的时间就能执行完毕。
Last modification:September 14th, 2018 at 06:27 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment