9、ReentrantReadWriteLock读写锁

9.1、悲观锁

当我们要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发的发生。为什么叫做悲观锁呢?因为这是一种对数据的修改抱有悲观态度的并发控制方式。我们一般认为数据被并发修改的概率比较大,所以需要在修改之前先加锁。

9.2、乐观锁

乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁,只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。乐观锁通常是通过在表中增加一个版本(version)或时间戳(timestamp)来实现,其中,版本最为常用。

9.3、表锁

我要操作某一条记录的时候,我害怕其它线程也来操作,我就把整个表锁起来。

9.4、行锁

我要操作某一条记录的时候,我害怕其它线程也来操作,我就把当前要操作的这一行记录锁起来。

9.5、读锁

共享锁,易发生死锁

9.6、写锁

独占锁,易发生死锁

9.7、总结

行锁,表锁,读锁,写锁,以及 syncronized 实现的锁均为悲观锁。

9.8、案例实现读写锁

不使用读写锁的情况

代码演示

package com.ae.juc.readWriteLock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

//创建资源类
class Cache1{
    //缓存
    private volatile Map<String,Object> cache = new HashMap<>();

    //写数据
    public void put(String key, Object value){
        System.out.println(Thread.currentThread().getName() + " 正在写操作 " + key);
        try {
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //写数据
        cache.put(key,value);
        System.out.println(Thread.currentThread().getName() + " 写完了 " + key);
    }

    //读数据
    public Object get(String key){
        System.out.println(Thread.currentThread().getName() + " 正在读取操作 " + key);
        Object value = null;
        try {
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //读数据
        value = cache.get(key);
        System.out.println(Thread.currentThread().getName() + " 读取完了 " + key);
        return value;
    }
}

/**
 * 问题:在没有写完的时候就来读了
 */
public class ReadWriteLockDemo1 {
    public static void main(String[] args) {
        Cache1 cache = new Cache1();

        //写数据
        for (int i = 1; i <= 5 ; i++) {
            final int num = i;
            new Thread(() -> {
                cache.put(num+"", num+"");
            },String.valueOf(i)).start();
        }

        //读数据
        for (int i = 1; i <= 5 ; i++) {
            final int num = i;
            new Thread(() -> {
                cache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

运行结果

1 正在写操作 1
3 正在写操作 3
4 正在写操作 4
5 正在写操作 5
2 正在写操作 2
1 正在读取操作 1
2 正在读取操作 2
4 正在读取操作 4
5 正在读取操作 5
3 正在读取操作 3
1 读取完了 1
4 写完了 4
5 读取完了 5
4 读取完了 4
2 读取完了 2
3 写完了 3
2 写完了 2
5 写完了 5
1 写完了 1
3 读取完了 3

使用读写锁的情况

代码演示

package com.ae.juc.readWriteLock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

//创建资源类
class Cache2{
    //缓存
    private volatile Map<String,Object> cache = new HashMap<>();

    //创建读写锁
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //写数据
    public void put(String key, Object value){
        //增加写锁
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在写操作 " + key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //写数据
            cache.put(key,value);
            System.out.println(Thread.currentThread().getName() + " 写完了 " + key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            readWriteLock.writeLock().unlock();
        }
    }

    //读数据
    public Object get(String key){
        //增加读锁
        readWriteLock.readLock().lock();
        Object value = null;
        try {
            System.out.println(Thread.currentThread().getName() + " 正在读取操作 " + key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //读数据
            value = cache.get(key);
            System.out.println(Thread.currentThread().getName() + " 读取完了 " + key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            readWriteLock.readLock().unlock();
        }
        return value;
    }
}

/**
 * 解决问题:使用读写锁来解决问题
 */
public class ReadWriteLockDemo2 {
    public static void main(String[] args) {
        Cache2 cache = new Cache2();

        //写数据
        for (int i = 1; i <= 5 ; i++) {
            final int num = i;
            new Thread(() -> {
                cache.put(num+"", num+"");
            },String.valueOf(i)).start();
        }

        //读数据
        for (int i = 1; i <= 5 ; i++) {
            final int num = i;
            new Thread(() -> {
                cache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

运行结果

2 正在写操作 2
2 写完了 2
1 正在写操作 1
1 写完了 1
3 正在写操作 3
3 写完了 3
5 正在写操作 5
5 写完了 5
4 正在写操作 4
4 写完了 4
4 正在读取操作 4
1 正在读取操作 1
2 正在读取操作 2
3 正在读取操作 3
5 正在读取操作 5
5 读取完了 5
3 读取完了 3
2 读取完了 2
4 读取完了 4
1 读取完了 1

9.9、读写锁

9.9.1、概念

9.9.2、读写锁解释

无锁的情况

多个线程同时去对某一个资源进行读写操作,会导致某些线程读取的数据不一致。这就会导致问题,所以引出了锁的概念。

加锁的情况:【synchronized、ReentrantLock】

这些锁都是独占的,在多线程情况下,不管你是读还是写,每一次只能有一个线程能拿到锁。那么问题来了,当很多线程都是同时来读,你没必要一次只能一个来读呀,效率极低,所以就引出读写锁。

读写锁的情况:【ReentrantReadWriteLock】

读写锁ReentrantReadWriteLock:读读共享,读写互斥,写写互斥_猿人小郑的博客-CSDN博客_读写锁读写互斥么

读写锁是将读锁和写锁分开,当很多线程同时来读,读读共享,线程同时读取,提高性能;当很多线程同时来写,写写互斥,只能一个线程一个线程的执行;当很多线程同时来读写,读写互斥,也只能一个线程一个线程的执行。所以,从整体的角度来看,读写锁ReentrantReadWriteLock在读多写少的情况下,还是能提升性能的。

9.10、读写锁的降级

ReentrantReadWriteLock 还允许通过获取写入锁定,然后读取锁定然后释放写入锁定从写入锁定到读取锁定。 但是,从读锁定升级到写锁是不可能的。简单说就是写锁可以降级为读锁,读锁不能生升级为写锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

了凡啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值