自学内容网 自学内容网

ReadWriteLock读写锁

读写锁基本概念

ReadWriteLock是Java并发包中的一个接口,它定义了两种锁:读锁(Read Lock)和写锁(Write Lock),真正的实现类是ReentrantReadWriteLock。读锁允许多个线程同时读取共享资源,而写锁则要求独占资源,即当一个线程持有写锁时,其他线程不能获取读锁或写锁。

读写锁的作用

在进行共享资源的并发访问时,不论是使用synchronized还是使用重入锁ReentrantLock,一次都只允许一个线程访问共享资源,但是很多时候我们只是要读取共享资源,并不修改共享资源,多个线程同时读取共享资源并不会产生不一致问题,而且还能提高并发效率,对共享资源修改时就只让一个线程进入,又保证数据的一致性,这就是读写锁ReadWriteLock诞生的原因。读写锁分离是一种常见的锁思想。例如在一些数据库内,利用读锁让多个事务可以同时读某行数据,并且数据不会被修改,利用写锁保证只有一个事务可以修改某行数据,并且不会被其它事务读取到脏数据。

写锁:也叫作排他锁,如果数据有加写锁,就只有持有写锁的线程才能对数据进行操作,数据加持着写锁时,其他线程不能加写锁,也不能施加读锁。

读锁:也叫作共享锁,多个线程可以对同一个数据添加多个读锁,数据被加上读锁后就不能再被加上写锁。

读写锁的使用

使用ReadWriteLock实现读写控制示例:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RWLock {
    public static final CountDownLatch countDownLatch = new CountDownLatch(10);
    public static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    //获取读锁
    public static final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
    //获取写锁
    public static final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        //8个线程模拟读操作
        for (int i = 0; i < 8; i++) {
            new Thread(() -> {
                readLock.lock();
                try {
                    Thread.sleep(1000); // 模拟读操作
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    readLock.unlock();
                }
            }).start();
        }
         //2个线程模拟写操作
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                writeLock.lock();
                try {
                    Thread.sleep(2000); // 模拟写操作
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    writeLock.unlock();
                }
            }).start();
        }
        //等待所有操作完成
        countDownLatch.await();
        long end = System.currentTimeMillis();
        System.out.println("任务完成,耗时:" + (end - start));
    }
}
//任务完成,耗时:3081

使用ReentrantLock实现读写控制示例:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

public class RWLock {
    public static final CountDownLatch countDownLatch = new CountDownLatch(10);
    public static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                lock.lock();
                try {
                    Thread.sleep(1000); // 模拟读操作
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }).start();
        }
        //等待所有操作完成
        countDownLatch.await();
        long end = System.currentTimeMillis();
        System.out.println("任务完成,耗时:" + (end - start));
    }
}
//任务完成,耗时:10155

以上两个示例中,使用ReadWriteLock实现的读写控制任务总耗时3秒左右,而使用ReentrantLock的实现的读写控制任务总耗时10秒左右,在这个例子中ReadWriteLock比ReentrantLock快了3倍多,这是否就意味着ReadWriteLock的性能就一定比ReentrantLock好呢?其实不是的,ReadWriteLock适用于读多写少的场景,如上面例子中使用了8个线程模拟读操作,2个线程模拟写操作就是读多写少的场景,在写操作密集的场景下ReadWriteLock的复杂性使它具有更大的锁开销。应该在不同的场景下选择合适的锁。


原文地址:https://blog.csdn.net/qq_38875964/article/details/142512608

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!