高效细粒度2Q缓存锁池及其封装实现

介绍了一种高效细粒度2Q锁池的实现方式,采用FIFO和LRU两种队列策略,以降低单节点环境下的本地锁消耗和分布式环境下的分布式锁请求次数。通过自定义LockUtil工具类,实现了锁的获取和管理,支持不同容量的锁池配置和公平锁选项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

意义

基于2Q缓存算法,实现本地更细粒度的、更低消耗的本地锁复用,分布式环境下可用于分布式锁的前置锁,减少分布式锁的请求次数和资源消耗

工作原理

当数据第一次访问时,2Q算法将数据缓存在FIFO队列里面,当数据第二次被访问时,则将数据从FIFO队列移到LRU队列里面,两个队列各自按照自己的方法淘汰数据。详细实现如下:

在这里插入图片描述

(1). 新访问的数据(LRU队列中不存在)插入到FIFO队列;

(2). 如果数据在FIFO队列中一直没有被再次访问,则最终按照FIFO规则淘汰;

(3). 如果数据在FIFO队列中被再次访问,则将数据移到LRU队列头部;

(4). 如果数据在LRU队列再次被访问,则将数据移到LRU队列头部;

(5). LRU队列淘汰末尾的数据。

实现

package com.tsd.dlmPlatform_common.util;

import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author hejun
 * @version v1.0
 * @Description 用于更细颗粒度控制ReentrantLock,并支持锁池2Q策略的工具类
 * @date 2023/3/2
 */
@Slf4j
public class LockUtil {
    // 锁池LRU缓存最大容量
    private static final int DEFAULT_LRU_MAX_SIZE = 64;
    // 锁池FIFO缓存最大容量
    private static final int DEFAULT_FIFO_MAX_SIZE = 64;
    // 初始锁池名称
    private static final String DEFAULT_POOL_NAME = "DEFAULT_LOCK_POOL";
    // 支持分段上锁的2Q策略锁池
    private static volatile Map<String, TwoQuesLockPool> POOL_MAP = new HashMap<>();

    /**
     * 获取指定锁
     * @param poolName 锁池名称(控制缓存重排序颗粒度)
     * @param key 锁名称(控制锁颗粒度)
     * @param fifoMaxSize fifo锁池最大容量
     * @param lruMaxSize lru锁池最大容量
     * @param isFair 是否公平
     * @return
     */
    public static ReentrantLock getLock(String poolName, String key, int fifoMaxSize, int lruMaxSize, boolean isFair){
        if(poolName == null || key == null || fifoMaxSize == 0 || lruMaxSize == 0){
            throw new RuntimeException("LockUtil.getLock() 方法参数有误");
        }
        return getOrAddLockPool(poolName, fifoMaxSize, lruMaxSize).getLock(key, isFair);
    }

    /**
     * 获取指定锁
     * @param poolName 锁池名称(控制缓存重排序颗粒度)
     * @param key 锁名称(控制锁颗粒度)
     * @param fifoMaxSize fifo锁池最大容量
     * @param lruMaxSize lru锁池最大容量
     * @return
     */
    public static ReentrantLock getLock(String poolName, String key, int fifoMaxSize, int lruMaxSize){
        return getLock(DEFAULT_POOL_NAME, key, fifoMaxSize, lruMaxSize, false);
    }

    /**
     * 获取指定锁
     * @param key 锁名称(控制锁颗粒度)
     * @param fifoMaxSize fifo锁池最大容量
     * @param lruMaxSize lru锁池最大容量
     * @param isFair 是否公平
     * @return
     */
    public static ReentrantLock getLock(String key, int fifoMaxSize, int lruMaxSize, boolean isFair){
        return getLock(DEFAULT_POOL_NAME, key, fifoMaxSize, lruMaxSize, isFair);
    }

    /**
     * 获取指定锁
     * @param key 锁名称(控制锁颗粒度)
     * @param fifoMaxSize fifo锁池最大容量
     * @param lruMaxSize lru锁池最大容量
     * @return
     */
    public static ReentrantLock getLock(String key, int fifoMaxSize, int lruMaxSize){
        return getLock(DEFAULT_POOL_NAME, key, fifoMaxSize, lruMaxSize, false);
    }

    /**
     * 获取指定锁
     * @param poolName 锁池名称(控制缓存重排序颗粒度)
     * @param key 锁名称(控制锁颗粒度)
     * @param isFair 是否公平
     * @return
     */
    public static ReentrantLock getLock(String poolName, String key, boolean isFair){
        return getLock(poolName, key, DEFAULT_FIFO_MAX_SIZE, DEFAULT_LRU_MAX_SIZE, isFair);
    }

    /**
     * 获取指定锁
     * @param poolName 锁池名称(控制缓存重排序颗粒度)
     * @param key 锁名称(控制颗粒度)
     * @return
     */
    public static ReentrantLock getLock(String poolName, String key){
        return getLock(poolName, key, DEFAULT_FIFO_MAX_SIZE, DEFAULT_LRU_MAX_SIZE, false);
    }

    /**
     * 获取指定锁
     * @param key 锁名称(控制颗粒度)
     * @return
     */
    public static ReentrantLock getLock(String key){
        return getLock(DEFAULT_POOL_NAME, key, DEFAULT_FIFO_MAX_SIZE, DEFAULT_LRU_MAX_SIZE, false);
    }

    /**
     * 获得指定名称的锁池,如果没有就创建
     * @param pooName
     * @param fifoMaxSize
     * @param lruMaxSize
     * @return
     */
    private static TwoQuesLockPool getOrAddLockPool(String pooName, int fifoMaxSize, int lruMaxSize){
        TwoQuesLockPool lockPool = POOL_MAP.get(pooName);
        // 双检锁的方式检查锁池是否存在
        if(lockPool == null){
            synchronized (pooName.intern()){
                lockPool = POOL_MAP.get(pooName);
                if(lockPool == null){
                    lockPool = new TwoQuesLockPool(fifoMaxSize, lruMaxSize);
                    POOL_MAP.put(pooName, lockPool);
                }
            }
        }
        return lockPool;
    }

    /**
     * 锁缓存池类,accessOrder 为true则构造LRU锁池,为false则构造FIFO锁池
     * @param <K>
     * @param <V>
     */
    @Getter
    private static class LockCachePool<K,V> extends LinkedHashMap<K,V> {

        // 触发回收的缓存池大小
        private int maxSize;

        private LockCachePool(int maxSize, boolean accessOrder) {
            super(maxSize, 0.75F, accessOrder);
            if(maxSize < 1){
                throw new RuntimeException("maxSize不能小于1");
            }
            this.maxSize = maxSize;
        }

        public static LockCachePool createLruLockCachePool(int maxSize){
            return new LockCachePool(maxSize, true);
        }

        public static LockCachePool createFifoLockCachePool(int maxSize){
            return new LockCachePool(maxSize, false);
        }


        @Override
        protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
            boolean removeEldestEntry = false;
            if(size() > maxSize){
                ReentrantLock lock = (ReentrantLock)eldest.getValue();
                // 当锁池容量超限,且队头的锁未被持有时,返回true,走模板方法逻辑删除头部节点
                if (!lock.isLocked()){
                    removeEldestEntry = true;
                }else {
                    // 当头部锁被持有时,手动删除最接近头部且未被持有的锁
                    Iterator<Map.Entry<K, V>> iterator = this.entrySet().iterator();
                    if(iterator.hasNext()){
                        // 指向头部锁后的节点
                        iterator.next();
                        while (iterator.hasNext() && size() > maxSize){
                            Map.Entry<K, V> eldestEntry = iterator.next();
                            ReentrantLock eldestLock = (ReentrantLock) eldestEntry.getValue();
                            if(!eldestLock.isLocked()){
                                super.remove(eldestEntry.getKey());
                            }
                        }
                    }
                }
            }
            return removeEldestEntry;
        }
    }

    /**
     * 2Q策略锁池类
     */
    public static class TwoQuesLockPool{
        //LRU锁池大小
        @Getter
        private int lruMaxSize = 64;
        //FIFO锁池大小
        @Getter
        private int fifoMaxSize = 64;
        //LRU锁池
        private LockCachePool<String, ReentrantLock> lruLockPool;
        //FIFO锁池
        private LockCachePool<String, ReentrantLock> fifoLockPool;
        //同步锁
        private final ReentrantLock mutex = new ReentrantLock();

        public TwoQuesLockPool(int fifoMaxSize, int lruMaxSize){
            this.fifoMaxSize = fifoMaxSize;
            this.lruMaxSize = lruMaxSize;
            this.lruLockPool = LockCachePool.createLruLockCachePool(this.lruMaxSize);
            this.fifoLockPool = LockCachePool.createFifoLockCachePool(this.fifoMaxSize);
        }
        public TwoQuesLockPool(){
            this.lruLockPool = LockCachePool.createLruLockCachePool(this.lruMaxSize);
            this.fifoLockPool = LockCachePool.createFifoLockCachePool(this.fifoMaxSize);
        }

        /**
         * 获取指定锁,默认优先获取LRU缓存中的锁
         * @param key 锁名称(控制颗粒度)
         * @param isFair 是否公平
         * @return
         */
        public ReentrantLock getLock(String key, boolean isFair){
            ReentrantLock lock = null;
            // LRU锁池get会影响排序,需加锁
            mutex.lock();
            try {
                lock = lruLockPool.get(key);
                if (lock == null){
                    lock = fifoLockPool.get(key);
                    if(lock != null){
                        lruLockPool.put(key, fifoLockPool.remove(key));
                    }else {
                        lock = new ReentrantLock(isFair);
                        fifoLockPool.put(key, lock);
                    }
                }
            }catch (Exception e){
                log.error(e.getMessage(), e);
                throw e;
            }finally {
                mutex.unlock();
            }
            System.out.println(key+"——>fifo——>"+ JSON.toJSONString(fifoLockPool.keySet()));
            System.out.println(key+"——>lru——>"+ JSON.toJSONString(lruLockPool.keySet()));
            return lock;
        }
    }

    public static void main(String[] args) {
        getLock("testkey1",3,3).lock();
        getLock("testkey2",3,3);
        getLock("testkey3",3,3);
        getLock("testkey4",3,3);
        getLock("testkey4",3,3);
        getLock("testkey3",3,3);
        getLock("testkey2",3,3);
        getLock("testkey1",3,3);
        getLock("testkey1",3,3);
        getLock("testkey3",3,3);

    }
}

输出:
testkey1——>fifo——>[“testkey1”]
testkey1——>lru——>[]
testkey2——>fifo——>[“testkey1”,“testkey2”]
testkey2——>lru——>[]
testkey3——>fifo——>[“testkey1”,“testkey2”,“testkey3”]
testkey3——>lru——>[]
testkey4——>fifo——>[“testkey1”,“testkey3”,“testkey4”]
testkey4——>lru——>[]
testkey4——>fifo——>[“testkey1”,“testkey3”]
testkey4——>lru——>[“testkey4”]
testkey3——>fifo——>[“testkey1”]
testkey3——>lru——>[“testkey4”,“testkey3”]
testkey2——>fifo——>[“testkey1”,“testkey2”]
testkey2——>lru——>[“testkey4”,“testkey3”]
testkey1——>fifo——>[“testkey2”]
testkey1——>lru——>[“testkey4”,“testkey3”,“testkey1”]
testkey1——>fifo——>[“testkey2”]
testkey1——>lru——>[“testkey4”,“testkey3”,“testkey1”]
testkey3——>fifo——>[“testkey2”]
testkey3——>lru——>[“testkey4”,“testkey1”,“testkey3”]

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值