Redission的读写锁探究

1,587 阅读4分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。

前言

ReentrantLock适合普通场合,如多次加锁,递归调用等,在一些读多写少的场景,我们只需要写的时候互斥就好,没必要全部互斥,所有接下来我们探究下redission的读写锁使用。

读写锁是什么?

在给一段临界区代码加锁,加锁是在进行写操作的时候才会互斥,而在进行读的时候是可以共享的进行访问临界区的

为什么需要读写锁?

有一些公共数据修改的机会比较少,而读的机会却是非常多的,此公共数据的操作基本都是读,如果每次操作都给此段代码加锁,太浪费时间了而且也很浪费资源,降低程序的效率。

因为读操作不会修改数据,只是做一些查询,所以在读的时候不用给此段代码加锁,可以共享的访问,只有涉及到写的时候,互斥的访问就行

读写锁适合于读多写少的场合,可以提高并发效率

读写锁的行为

读写之间是互斥的—–>读的时候写阻塞,写的时候读阻塞,而且读和写在竞争锁的时候,写会优先得到锁

三种关系

  • 读和读之间共享关系
  • 写和写之间是互斥关系
  • 读和写之间是互斥关系

java中的读写锁使用

jdk中提供了读写锁ReentrantReadWriteLock,下面是使用例子

ReentrantReadWriteLock

/**
 * @Description: 读写锁
 * @Author: jianweil
 * @date: 2022/1/27 20:04
 */
public class ReadWriteLockTest {

    private static ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
    //读锁
    private static ReentrantReadWriteLock.ReadLock readLock = reentrantLock.readLock();
    //写锁
    private static ReentrantReadWriteLock.WriteLock writeLock = reentrantLock.writeLock();

    public static void read() {
        readLock.lock();
        try {
            System.out.println(LocalDateTime.now()+"    "+ Thread.currentThread().getName() + "获取读锁,开始执行");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(LocalDateTime.now()+"    "+ Thread.currentThread().getName() + "释放读锁");
            readLock.unlock();
        }
    }

    public static void write() {
        writeLock.lock();
        try {
            System.out.println(LocalDateTime.now()+"    "+ Thread.currentThread().getName() + "获取写锁,开始执行");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(LocalDateTime.now()+"    "+ Thread.currentThread().getName() + "释放写锁");
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        new Thread(() -> read(), "Thread1").start();
        new Thread(() -> read(), "Thread2").start();
        new Thread(() -> write(), "Thread3").start();
        new Thread(() -> write(), "Thread4").start();
    }
}

测试结果

2022-01-27T20:14:24.427    Thread1获取读锁,开始执行
2022-01-27T20:14:24.427    Thread2获取读锁,开始执行
2022-01-27T20:14:25.427    Thread1释放读锁
2022-01-27T20:14:25.427    Thread2释放读锁
2022-01-27T20:14:25.427    Thread3获取写锁,开始执行
2022-01-27T20:14:26.427    Thread3释放写锁
2022-01-27T20:14:26.427    Thread4获取写锁,开始执行
2022-01-27T20:14:27.428    Thread4释放写锁

可以看出, Thread1和 Thread2是执行的读操作,所以是可以同时访问的,不会阻塞。

而Thread3和Thread4执行的是写锁,Thread3先获得写锁,要等Thread3执行完成释放写锁,Thread4才能获取写锁执行。

redission中读写锁

RReadWriteLock

/**
 * @Description: 读写锁
 * @Author: jianweil
 * @date: 2022/1/27 20:04
 */
public class ReadWriteLockTest2 {
    private static final String KEY_LOCKED = "myLock";
    private static RedissonClient redissonClient = null;

    private static void initRedissonClient() {
        // 1. Create config object
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        // 2. Create Redisson instance
        ReadWriteLockTest2.redissonClient = Redisson.create(config);
    }

    public static void read() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(KEY_LOCKED);
        readWriteLock.readLock().lock();
        try {
            System.out.println(LocalDateTime.now()+"    "+ Thread.currentThread().getName() + "获取读锁,开始执行");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(LocalDateTime.now()+"    "+ Thread.currentThread().getName() + "释放读锁");
            readWriteLock.readLock().unlock();
        }
    }

    public static void write() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(KEY_LOCKED);
        readWriteLock.writeLock().lock();
        try {
            System.out.println(LocalDateTime.now()+"    "+ Thread.currentThread().getName() + "获取写锁,开始执行");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(LocalDateTime.now()+"    "+ Thread.currentThread().getName() + "释放写锁");
            readWriteLock.writeLock().unlock();
        }
    }

    public static void main(String[] args) {
        initRedissonClient();
        new Thread(() -> read(), "Thread1").start();
        new Thread(() -> read(), "Thread2").start();
        new Thread(() -> write(), "Thread3").start();
        new Thread(() -> write(), "Thread4").start();
    }
}

测试结果

2022-01-27T20:33:15.295    Thread4获取写锁,开始执行
2022-01-27T20:33:16.296    Thread4释放写锁
2022-01-27T20:33:16.300    Thread2获取读锁,开始执行
2022-01-27T20:33:16.300    Thread1获取读锁,开始执行
2022-01-27T20:33:17.301    Thread2释放读锁
2022-01-27T20:33:17.301    Thread1释放读锁
2022-01-27T20:33:17.305    Thread3获取写锁,开始执行
2022-01-27T20:33:18.306    Thread3释放写锁

Thread4和Thread3是写锁,互斥,所以Thread4执行完成释放写锁,其他线程才能获取读锁或者写锁,因为Thread2和Thread1在Thread4释放写锁获得了读锁,读读是共享的,所以可以交替执行。而Thread3写锁必定在Thread4写锁之后执行。

总结

在redission中封装的读写锁和jdk中的读写锁ReentrantReadWriteLock使用逻辑是一样的。只不过redission中的读写锁是基于redis实现了分布式,功能也更加强大。

写在最后

  • 👍🏻:有收获的,点赞鼓励!
  • ❤️:收藏文章,方便回看!
  • 💬:评论交流,互相进步!