JUC学习---ReentrantReadWriteLock

在锁中有乐观锁和悲观锁

悲观锁会将资源锁住,不能多个线程同时的去访问,效率较低

乐观锁会在资源上加一个版本号,每次进行修改,会将版本号进行对比,如果相等,则进行修改,实际上,并不会对资源进行加锁底层时CAS,使用了一个自旋的方式去进行对比,交换

对于表锁,行锁

表锁会将一张表锁住,但行锁只会将一行内容上锁

但表锁不会造成死锁,因为死锁是因为多个线程将资源拿到,等待对方释放,再释放,但对于表锁,只有一个线程拿到资源,并不能造成死锁

但行锁可以造成死锁,比如第一个线程将第一行锁住,想要获取第二行,但第二个线程将第二行锁住,想要获取第一行,资源一直得不到释放,即发生死锁

对于写锁和读锁

写锁时独占锁,而读锁时共享锁,但两者都可能会造成死锁

对于写锁,线程一对第一行加读锁,线程二对第一行加读锁,但线程一,二都在等待对方的释放进行修改操作,所以会导致死锁

对于写锁,第一个线程将第一行锁住,想要获取第二行,但第二个线程将第二行锁住,想要获取第一行,资源一直得不到释放,即发生死锁

下面用一个例子来显示一下读写锁

当没有加读写锁时

public class WRDemo {
    public static void main(String[] args) {
        cache cache = new cache();
        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                final String num= finalI +"";
                try {
                    cache.put(num,num);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            },String.valueOf(i)).start();
        }

        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{

                final String num= finalI +"";
                try {
                    cache.get(num);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            },String.valueOf(i)).start();
        }
    }
}
class cache{
    private static final HashMap<String,Object> MAP=new HashMap<>();

    public void put(String key,Object value) throws InterruptedException {
        System.out.println(key+"号线程开始写");
        TimeUnit.SECONDS.sleep(3);
        MAP.put(key,value);
        System.out.println(key+"号线程写完");
    }

    public void get(String key) throws InterruptedException {
        Object result=null;
        System.out.println(key+"号线程开始读");
        TimeUnit.SECONDS.sleep(3);
        result= MAP.get(key);
        System.out.println(key+"号线程读到了"+result);
    }
}

结果显示:一号线程没有写完时,就有读一号线程,所以读到了null

所以要引入读写锁ReentrantReadWriteLock

代码如下:

public class WRDemo {
    public static void main(String[] args) {
        cache cache = new cache();
        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                final String num= finalI +"";
                cache.put(num,num);

            },String.valueOf(i)).start();
        }

        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{

                final String num= finalI +"";
                cache.get(num);
            },String.valueOf(i)).start();
        }
    }
}
class cache{
    private static final HashMap<String,Object> MAP=new HashMap<>();

    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    public void put(String key,Object value){
        readWriteLock.writeLock().lock();//加写锁
        try {
            System.out.println(key+"号线程开始写");
            TimeUnit.SECONDS.sleep(3);
            MAP.put(key,value);
            System.out.println(key+"号线程写完");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            readWriteLock.writeLock().unlock();
        }

    }

    public void get(String key){

        readWriteLock.readLock().lock();

        try {
            Object result=null;
            System.out.println(key+"号线程开始读");
            TimeUnit.SECONDS.sleep(3);
            result= MAP.get(key);
            System.out.println(key+"号线程读到了"+result);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            readWriteLock.readLock().unlock();
        }

    }
}

结果:当写完后才可以读

为什么会有读写锁

当无锁时,多个线程共同争抢一个资源,会显得特别的乱

而当有锁时比如synchronized和ReentrantLock是独占的,每次都只能是一个操作

但当引入读写锁时,问题就不一样了,即在现实中,读操作远远大于写操作,所以引入了读锁是共享的,而写锁是独占的

 再来讨论一下读写锁降级

写锁可以降级到读锁,但读锁不可以升级到写锁

开始写----->开始读------>写完------->读完

就比如抄作业,可以当一个人边写,你边抄,当他写完了,你也抄完了

我们来验证一下

        ReadWriteLock lock=new ReentrantReadWriteLock();

        lock.writeLock().lock();
        System.out.println("开始写");
        lock.readLock().lock();
        System.out.println("写完了");
        lock.writeLock().unlock();
        lock.readLock().unlock();

结果如下

但如果先读再写:

        ReadWriteLock lock=new ReentrantReadWriteLock();


        lock.readLock().lock();
        lock.writeLock().lock();
        System.out.println("开始写");
        lock.readLock().unlock();
        System.out.println("写完了");
        lock.writeLock().unlock();

 结果:从而不能降级

 如果需要这个demo则:https://gitee.com/wuwenlei/annotation-development/tree/dev/spring-annotation/src/main/java/com/springannotation/ReentrantReadWriteLockDemo

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值