读写锁详细介绍

1. 简单介绍

Java中,读写锁(ReadWriteLock)是一种高级锁机制,允许多个线程同时读取共享资源,而写入共享资源时需要独占访问。读写锁的主要目的是提高并发性和性能,特别是在读操作远多于写操作的情况下。Java中的ReadWriteLock接口及其主要实现类ReentrantReadWriteLock提供了这种功能。

读写锁主要是为了解决读共享的问题。如果不使用读写锁且此时有很多读的请求,那么每次读请求也会进行加锁释放锁完成同步操作,降低了处理速度。而读写锁的读锁是共享锁,可以使得多个线程同时读取共享资源。

2. ReentrantReadWriteLock实现类

ReentrantReadWriteLockReadWriteLock接口的主要实现类,它提供了可重入的读写锁。

主要特点

  • 可重入:同一线程可以多次获取读锁或写锁。
  • 公平和非公平模式:默认情况下,ReentrantReadWriteLock是非公平的,但可以通过构造函数指定为公平锁。
  • 读锁共享:多个线程可以同时持有读锁。
  • 写锁独占:写锁是独占的,一次只能由一个线程持有。

构造方法

  • ReentrantReadWriteLock(): 创建一个非公平的读写锁。
  • ReentrantReadWriteLock(boolean fair): 创建一个指定公平性的读写锁。

主要方法

  • readLock(): 返回用于读操作的锁。
  • writeLock(): 返回用于写操作的锁。

3. 代码示例


import java.util.concurrent.locks.ReentrantReadWriteLock;

import static java.lang.Thread.sleep;

public class Test {
    private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private static final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private static final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
    private static int value;

    public static int readValue() {
        readLock.lock();
        try {
            // 模拟读取操作
            System.out.println(Thread.currentThread().getName() +  "Start read value " );
            sleep(1000L);
            System.out.println(Thread.currentThread().getName() +  "Read value: " + value);
            return value;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            readLock.unlock();
        }
    }

    public static void writeValue(int newValue) {
        writeLock.lock();
        try {
            // 模拟写入操作
            System.out.println(Thread.currentThread().getName() + "Start wrote new value" );
            value = newValue;
            sleep(1000L);
            System.out.println(Thread.currentThread().getName() + "Wrote new value" + value);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {

        // 创建多个读线程
        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                readValue();
            }
        }).start();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                readValue();
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                int num = (int) (Math.random() * 100);
                writeValue(num);
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                int num = (int) (Math.random() * 100);
                writeValue(num);
            }
        }).start();
    }
}

可以看到,我们定义了一个变量value用来模拟共享资源,且定义了两个函数用来模拟对该共享资源的读操作和写操作。在main函数里,创建了读线程和写线程,对该共享资源进行访问。

结果

Thread-0Start read value
Thread-1Start read value
Thread-0Read value: 0
Thread-1Read value: 0
Thread-2Start wrote new value
Thread-2Wrote new value50
Thread-2Start wrote new value
Thread-2Wrote new value7
Thread-2Start wrote new value
Thread-2Wrote new value78
Thread-2Start wrote new value
Thread-2Wrote new value6
Thread-2Start wrote new value
Thread-2Wrote new value91
Thread-3Start wrote new value
Thread-3Wrote new value32
Thread-3Start wrote new value
Thread-3Wrote new value94
Thread-3Start wrote new value
Thread-3Wrote new value90
Thread-3Start wrote new value
Thread-3Wrote new value84
Thread-3Start wrote new value
Thread-3Wrote new value3
Thread-0Start read value
Thread-1Start read value
Thread-1Read value: 3
Thread-1Start read value
Thread-0Read value: 3
Thread-0Start read value
Thread-1Read value: 3
Thread-1Start read value
Thread-0Read value: 3
Thread-0Start read value
Thread-1Read value: 3
Thread-1Start read value
Thread-0Read value: 3
Thread-0Start read value
Thread-1Read value: 3
Thread-0Read value: 3

从结果中可以看到,读操作可以同时进行,而写操作必须独占访问。

4. 锁降级

4.1 简单介绍

锁降级是指从写锁降级为读锁的过程,这是允许的,但锁升级(从读锁升级为写锁)通常是不允许的。锁降级通常是为了在修改共享资源后,保持一定的读访问,而不完全释放对资源的保护。其工作原理如下

  1. 持有写锁
    一开始,线程需要持有写锁,以进行写操作。这是因为写操作需要独占访问共享资源,防止数据不一致
  2. 获取读锁
    在持有写锁的同时,线程可以尝试获取读锁。由于线程已经持有写锁,因此获取读锁不会被阻塞。此时,线程同时持有写锁和读锁。
  3. 释放写锁
    在获取读锁之后,线程可以释放写锁,此时线程仍然持有读锁。这样做的好处是,写操作已经完成,资源处于一致状态,线程仍然可以继续读取资源,同时允许其他读线程访问资源,但写线程必须等待。

4.2 代码示例


import java.util.concurrent.locks.ReentrantReadWriteLock;

import static java.lang.Thread.sleep;

public class Test {
    private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private static final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private static final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
    private static int value;

    public static void writeAndRead() {
        writeLock.lock();
        try {
            // 模拟写操作
            value++;
            System.out.println("Write: " + value);

            // 锁降级 - 在持有写锁的同时获取读锁
            readLock.lock();
        } finally {
            // 释放写锁,但仍然持有读锁
            writeLock.unlock();
        }

        try {
            // 模拟读操作
            System.out.println("Read after write: " + readValue());
        } finally {
            // 最终释放读锁
            readLock.unlock();
        }
    }

    public static int readValue() {
        readLock.lock();
        try {
            // 模拟读取操作
            return value;
        } finally {
            readLock.unlock();
        }
    }

    public static void main(String[] args) {

        Thread thread = new Thread(() -> {
            writeAndRead();
        });
        thread.start();
    }
}

工作流程

  • 获取写锁:writeLock.lock() 用于开始写操作,确保当前线程对共享资源的独占访问。
  • 执行写操作:修改共享资源,增加value
  • 获取读锁:在持有写锁的同时获取读锁,这确保了数据的一致性。
  • 释放写锁:在获取读锁之后,释放写锁。此时,线程仍然持有读锁,可以继续读取资源。
  • 执行读操作:在持有读锁的情况下读取资源。
  • 释放读锁:完成读操作后,释放读锁。

通过锁降级,写线程在完成写操作后仍然可以继续读取资源,同时允许其他读线程并发地读取资源,提高了系统的并发性和性能。

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值