使用弱引用实现细粒度锁

使用弱引用实现细粒度锁

本文参考
引用1:https://www.jianshu.com/p/825cca41d962
引用2:https://blog.csdn.net/xiaoliuliu2050/article/details/73992089

1、弱引用

    Java中的引用有四种:强引用、软引用、弱引用和虚引用,对应的强度(垃圾回收时的判断依据)依次递减。

  1. 强引用
        强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用 对象来解决内存不足的问题。
    引用方式:
    Object o = new Object();  // 强引用方式
    o = null; // 需显式的 将 o 赋值为 null,垃圾回收才会回收此对象的内存数据 
    
  2. 软引用
        软引用是用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。也就是软引用在jvm内存足够的情况下,和强引用效果一致。
    引用方式:
    Object o = new Object();  // 强引用方式
    SoftReference<Object> softReference = new SoftReference<>(o); // 给 o 指向的对象添加软引用
    o = null; // 删除 o 指向的对象的强引用
    Object o1 = softReference.get(); // 通过软引用对象获取值(在内存足够的情况下,都是必然可以获取的),如果获取到,将引用类型提升为强引用
    
  3. 弱引用
        弱引用也是用来描述非必须对象的,他的强度比软引用更弱一些,被弱引用关联的对象,在垃圾回收时,如果这个对象只被弱引用关联(没有任何强引用关联他),那么这个对象就会被回收。在使用弱引用保存对象值时,不能保证每次都能获取到,但是一旦获取到值,并用强引用关联,那么在关联的持续期间,我们都将可以从对应的弱引用中获取到对象值,直到这个对象值不存在强引用。
    引用方式:
    Object o = new Object();  // 强引用方式
    WeakReference<Object> weakReference = new WeakReference<>(o); // 给 o 指向的对象添加软引用
    o = null; // 删除 o 指向的对象的强引用
    Object o1 = weakReference.get(); // 获取弱引用的关联对象,这一步不能保证获取到值,一旦获取到值,那么后续的weakReference.get()返回的对象值都将和o1指向同一对象
    
  4. 虚引用
        一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。虚引用和弱引用对关联对象的回收都不会产生影响,如果只有虚引用或者弱引用关联着对象,那么这个对象就会被回收。它们的不同之处在于弱引用的get方法,虚引用的get方法始终返回null,弱引用可以使用ReferenceQueue,虚引用必须配合ReferenceQueue使用。

2、细粒度锁

    java中的几种锁:synchronized,ReentrantLock,ReentrantReadWriteLock已基本可以满足编程需求,但其粒度都太大,同一时刻只有一个线程能进入同步块,这对于某些高并发的场景并不适用。比如数据访问量的增加,需要根据当前值 +1进行设置,查询当前值,执行+1,设置访问量(这里只做举例,有其他方式可以避免这种操作),需要对代码加锁处理,防止数据库保存的访问量少于实际的访问量,但是如果对整块业务代码加synchronized锁,针对不同的数据操作,这样的加锁时没有意义的,而且还严重影响并发性能,我们只需要的同一数据的操作进行加锁处理即可,不会影响不同数据之间的并发处理,这也就是细粒度锁的应用场景。
    根据细粒度锁的应用场景,很多的锁在使用之后都将变为无用状态,可以被jvm回收处理。比如 针对 id=1的数据进行细粒度加锁,在锁使用完成之后,id=1对应的锁就可以被回收处理,且在锁的使用过程中,其余线程尝试获取id=1对应的锁时,和当前线程所持有的锁对象必须为同一个,这就正好与弱引用的特性一致。

3、实现

使用弱引用来实现细粒度锁:

public class WeakRefHashLock {

    /**
     * 存储 key 对应 锁 的弱引用
     */
    private ConcurrentHashMap<Object, LockWeakReference> lockMap = new ConcurrentHashMap<>();

    /**
     * 存储已过期的 ref
     */
    private ReferenceQueue<ReentrantLock> queue = new ReferenceQueue<>();

    /**
     * 获取 key 对应的 lock
     * @param key
     * @return
     */
    public Lock lock(Object key){

        if (lockMap.size() > 1000){
            clearEmptyRef();
        }

        // 获取 key 对应的 lock 弱引用
        LockWeakReference weakReference = lockMap.get(key);

        // 获取lock
        ReentrantLock lock = (weakReference == null ? null : weakReference.get());

        // 这里使用 while 循环获取,防止在获取过程中lock被gc回收
        while (lock == null){
            // 使用 putIfAbsent,在多线程环境下,针对同一 key ,weakReference 均指向同一弱引用对象
            // 这里使用 可重入锁
            weakReference = lockMap.putIfAbsent(key, new LockWeakReference(key, new ReentrantLock(), queue));

            // 获取弱引用指向的lock,这里如果获取到 lock 对象值,将会使 lock 对象值的弱引用提升为强引用,不会被gc回收
            lock = (weakReference == null ? null : weakReference.get());

            // 在 putIfAbsent 的执行和 weakReference.get() 执行的间隙,可能存在执行gc的过程,会导致 lock 为null,所以使用while循环获取
            if (lock != null){
                return lock;
            }

            // 获取不到 lock,移除map中无用的ref
            clearEmptyRef();

        }
        return lock;
    }

    /**
     * 清除 map 中已被回收的 ref
     */
    void clearEmptyRef(){

        Reference<? extends ReentrantLock> ref;
        while ((ref = queue.poll()) != null){
            LockWeakReference lockWeakReference = (LockWeakReference) ref;
            lockMap.remove(lockWeakReference.key);
        }

    }

    class LockWeakReference extends WeakReference<ReentrantLock>{

        /**
         * 存储 弱引用 对应的 key 值,方便 之后的 remove 操作
         */
        private Object key;

        public LockWeakReference(Object key, ReentrantLock lock, ReferenceQueue<? super ReentrantLock> q) {
            super(lock, q);
            this.key = key;
        }

    }

}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值