0
点赞
收藏
分享

微信扫一扫

JUC——读写锁

静鸡鸡的JC 2022-02-11 阅读 60
javajuc

目录

读写锁介绍

读锁又叫共享锁,可以允许多个线程读数据;写锁又叫做独占锁,只能允许一个线程进行写操作。

读锁和写锁都可能引起死锁现象:
在这里插入图片描述
在这里插入图片描述
所以为避免死锁现象,对相应数据上什么锁,就只对相应数据做什么操作。

ReentrantReadWriteLock

读锁在同一时刻可以允许多个读线程获取,即允许多个读线程同时上锁读取数据,不允许写线程获取操作。(上多个读锁:允许多个读线程,不允许写线程)

写锁同一时刻只能有一个写线程获取成功,其他都会被阻塞。(上一个写锁:只允许一个写线程,不允许其他读、写线程)

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 MyCache {
    private volatile Map<String, Object> map = new HashMap<>();
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void put(String key, Object value) {
        rwLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在写" + key);
            //暂停一会儿线程
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 写完了" + key);
            System.out.println();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.writeLock().unlock();
        }

    }

    public Object get(String key) {
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在读" + key);
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 读完了" + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.readLock().unlock();
        }
        return result;
    }
}

public class ReadWriteLockDemo {


    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 1; i <= 5; i++) {
            final int num = i;
            new Thread(() -> {
                myCache.put(num + "", num + "");
            }, String.valueOf(i)).start();
        }
        for (int i = 1; i <= 5; i++) {
            final int num = i;
            new Thread(() -> {
                myCache.get(num + "");
            }, String.valueOf(i)).start();
        }
    }
}

在这里插入图片描述

COW和读写锁对比

CopyOnWrite缺点:
内存占用问题:因为CopyOnWrite的写时复制机制每次进行写操作的时候都会有两个数组对象的内存,如果这个数组对象占用的内存较大的话,如果频繁的进行写入就会造成频繁的Yong GC和Full GC,此时应该考虑其他的容器,例如 ConcurrentHashMap。

数据一致性问题:
CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。读操作的线程可能不会立即读取到新修改的数据,因为修改操作发生在副本上。但最终修改操作会完成并更新容器所以这是最终一致性。当时有说到解决这两个缺点我们可以使用Collections.synchronizedList()来替代,找个无非就是对list的增删改查方法都加了synchronized实现。我们知道synchronized其实是一个独占锁 (排他锁),但是这样的话就会存在一个性能问题,如果对于读多写少的场景,每次读也要去获取锁,读完了之后再释放锁,这样就造成了每个读的请求都要进行获取锁,但是读的话并不会引起数据不安全,这样就会造成一个性能瓶颈。为了解决这个问题,就又出现了一种新的锁,读写锁(ReadWriteLock)。

举报

相关推荐

0 条评论