java key 粒度加锁工具,有考虑其他如redis锁切换扩展

 首先是key粒度的加锁工具

第一种实现:

public class ReentrantKeyLock {


    public Lock getLock(String lockKey) {
        Assert.notNull(lockKey, "lockKey不能为空");
        return new KeyLock(lockKey);
    }

    public static class KeyLock implements Lock, java.io.Serializable {

        //lockKey和对应的锁信息集合
        private static final Map<String, LockInfo> LOCK_INFO_MAP = Maps.newHashMap();

        private final String lockKey;

        public KeyLock(String lockKey) {
            this.lockKey = lockKey;
        }

        @Override
        public void lock() {
            while (true) {
                LockInfo lockInfo = getLockInfo(lockKey);
                lockInfo.lockCount.incrementAndGet();
                lockInfo.lock.lock();
                //如果锁以失效则重新找lockKey对应的新锁加锁
                if (!lockInfo.isValid) {
                    lockInfo.lock.unlock();
                } else {
                    break;
                }
            }

        }

        @Override
        public void lockInterruptibly() throws InterruptedException {
            while (true) {
                LockInfo lockInfo = getLockInfo(lockKey);
                lockInfo.lockCount.incrementAndGet();
                lockInfo.lock.lockInterruptibly();
                //如果锁以失效则重新找lockKey对应的新锁加锁
                if (!lockInfo.isValid) {
                    lockInfo.lock.unlock();
                } else {
                    break;
                }
            }
        }

        @Override
        public boolean tryLock() {
            while (true) {
                LockInfo lockInfo = getLockInfo(lockKey);
                lockInfo.lockCount.incrementAndGet();
                boolean isSuccess = lockInfo.lock.tryLock();
                //如果锁以失效则重新找lockKey对应的新锁加锁
                if (isSuccess && !lockInfo.isValid) {
                    lockInfo.lock.unlock();
                } else {
                    return isSuccess;
                }
            }
        }

        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            while (true) {
                LockInfo lockInfo = getLockInfo(lockKey);
                lockInfo.lockCount.incrementAndGet();
                boolean isSuccess = lockInfo.lock.tryLock(time, unit);
                //如果锁以失效则重新找lockKey对应的新锁加锁
                if (isSuccess && !lockInfo.isValid) {
                    lockInfo.lock.unlock();
                } else {
                    return isSuccess;
                }
            }
        }

        @Override
        public void unlock() {
            LockInfo lockInfo = getLockInfo(lockKey);
            int lockCount = lockInfo.lockCount.decrementAndGet();
            if (lockCount == 0) {
                //如果加锁线程数为 0 则从LOCK_INFO_MAP移除对应的锁
                LOCK_INFO_MAP.remove(lockKey);
                lockInfo.isValid = false;
            }
            lockInfo.lock.unlock();
        }

        @Override
        public Condition newCondition() {
            return getLockInfo(lockKey).lock.newCondition();
        }

        private LockInfo getLockInfo(String lockKey) {
            LockInfo lockInfo = LOCK_INFO_MAP.get(lockKey);
            if (Objects.isNull(lockInfo)) {
                synchronized (LOCK_INFO_MAP) {
                    lockInfo = LOCK_INFO_MAP.get(lockKey);
                    if (Objects.isNull(lockInfo)) {
                        lockInfo = new LockInfo();
                        LOCK_INFO_MAP.put(lockKey, lockInfo);
                    }
                }
            }
            return lockInfo;
        }
    }

    private static class LockInfo {
        private final AtomicInteger lockCount;
        private final ReentrantLock lock;
        //如果 LockInfo 从 LOCK_INFO_MAP 移除则被改为 false ,表示该锁已经失效
        private boolean isValid = true;

        public LockInfo() {
            lockCount = new AtomicInteger();
            lock = new ReentrantLock();
        }
    }
}

第二种实现:思路是从Stack Overflow某人回答来的.大体代码和他类似

public class ReentrantKeyLock1 {

    public Lock getLock(String lockKey) {
        Assert.notNull(lockKey, "lockKey不能为空");
        return new KeyLock(lockKey);
    }

    public static class KeyLock implements Lock, java.io.Serializable {

        //lockKey和对应的锁信息集合
        private static final Map<String, LockInfo> LOCK_INFO_MAP = Maps.newConcurrentMap();

        private final String lockKey;

        public KeyLock(String lockKey) {
            this.lockKey = lockKey;
        }

        @Override
        public void lock() {
            LockInfo lockInfo = getLockInfo(true);
            lockInfo.lock.lock();
        }

        @Override
        public void lockInterruptibly() throws InterruptedException {
            LockInfo lockInfo = getLockInfo(true);
            lockInfo.lock.lockInterruptibly();
        }

        @Override
        public boolean tryLock() {
            LockInfo lockInfo = getLockInfo(true);
            boolean isSuccess = lockInfo.lock.tryLock();
            if (!isSuccess) {
                cleanCacheLock(lockInfo);
            }
            return isSuccess;
        }

        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            LockInfo lockInfo = getLockInfo(true);
            boolean isSuccess = lockInfo.lock.tryLock(time, unit);
            if (!isSuccess) {
                cleanCacheLock(lockInfo);
            }
            return isSuccess;
        }

        @Override
        public void unlock() {
            LockInfo lockInfo = getLockInfo(false);
            lockInfo.lock.unlock();
            cleanCacheLock(lockInfo);
        }

        @Override
        public Condition newCondition() {
            return getLockInfo(false).lock.newCondition();
        }


        /**
         * 清除 LOCK_MAP_INFO 里 lockKey 对应的数据
         * 需要从 LOCK_INFO_MAP 中移除 key ,并把 currentLockInfo 设为null
         *
         * @param lockInfo
         */
        private void cleanCacheLock(LockInfo lockInfo) {
            //只有当加锁次数为 0 时才清除锁信息
            if (lockInfo.lockCount.decrementAndGet() == 0) {
                LOCK_INFO_MAP.compute(lockKey, (key, cacheLockInfo) -> {
                    if (cacheLockInfo == null || cacheLockInfo.lockCount.get() == 0) {
                        return null;
                    }
                    return cacheLockInfo;
                });
            }
        }

        /**
         * 获取锁信息
         *
         * @param isLock 为 true 时,加锁次数+1
         * @return LockInfo
         */
        private LockInfo getLockInfo(boolean isLock) {
            if (isLock) {
                //currentLockInfo不存在就要创建锁,get and put 非原子操作所以需要加synchronized
                return LOCK_INFO_MAP.compute(lockKey, (key, lockInfo) -> {
                    if (lockInfo == null) {
                        lockInfo = new LockInfo();
                    }
                    lockInfo.lockCount.incrementAndGet();
                    return lockInfo;
                });
            } else {
                LockInfo lockInfo = LOCK_INFO_MAP.get(lockKey);
                Assert.notNull(lockInfo, "可能是由于没有执行 lock 操作");
                return lockInfo;
            }
        }

        private static class LockInfo {
            private final AtomicInteger lockCount;
            private final ReentrantLock lock;

            public LockInfo() {
                lockCount = new AtomicInteger();
                lock = new ReentrantLock();
            }
        }
    }
}

 上面两种不一样的地方主要是用于判断是否要从map中释放key的lockCount的自增点不一样。

下面的类主要是为了方便后面切换成redis或其他锁时不需要所有地方的代码都要改

LockManager接口:可以有不同的实现类返回redis的lock、zookeeper的lock或上面实现的ReentrantKeyLock

public interface LockManager {

    /**
     * 获取锁
     *
     * @param key 需要锁的key
     * @return
     */
    Lock getLock(String key);

}

KeyLockManager:返回 ReentrantKeyLock的Lock对于的LockManager

public class KeyLockManager implements LockManager {

    private ReentrantKeyLock reentrantKeyLock = new ReentrantKeyLock();

    @Override
    public Lock getLock(String key) {
        Assert.notNull(key, "key不能为空");
        Lock lock = reentrantKeyLock.getLock(key);
        return lock;
    }

}

 工厂类:

public class LockFactory {

    private static LockManager lockManager;

    public static void initLockManager(LockManager lockManager) {
        Assert.isNull(LockFactory.lockManager, "lockManager只能初始化一次");
        synchronized (LockFactory.class) {
            Assert.isNull(LockFactory.lockManager, "lockManager只能初始化一次");
            LockFactory.lockManager = lockManager;
        }
    }

    public static Lock getLock(String key) {
        Assert.notNull(lockManager, "请初始化lockManager");
        return lockManager.getLock(key);
    }
}

在spring中的使用示例

    @Bean
    public LockManager lockManager() {
        //后面只要此处改成Redis对应的LockManager,就可以切换成redis的分布式锁.
        LockManager lockManager = new KeyLockManager();
        LockFactory.initLockManager(lockManager);
        return lockManager;
    }


    public void test() {
        Lock keyA = LockFactory.getLock("keyA");
        keyA.lock();
        System.out.println(123);
        keyA.unlock();
    }

当然也可以在其他生命周期创建,比如CommandListen等

测试类

public class LockTest {

    public static void main(String[] args) {
        LockManager lockManager = new KeyLockManager();
        LockFactory.initLockManager(lockManager);
        Lock keyA = LockFactory.getLock("keyA");
        Lock keyB = LockFactory.getLock("keyB");
        Lock keyC = LockFactory.getLock("keyC");
        Lock keyD = LockFactory.getLock("keyD");
        Lock keyE = LockFactory.getLock("keyE");
        for (int i = 0; i <= 100000; i++) {
            new MyThread("keyA", "keyA线程" + i, keyA).start();
            new MyThread("keyB", "keyB线程" + i, keyB).start();
            new MyThread("keyC", "keyC线程" + i, keyC).start();
            new MyThread("keyD", "keyD线程" + i, keyD).start();
            new MyThread("keyE", "keyE线程" + i, keyE).start();
        }
//        new MyThread("keyA", "keyA线程1").start();
//        new MyThread("keyA", "keyA线程2").start();
//        new MyThread("keyB", "keyB线程1").start();
//        new MyThread("keyB", "keyB线程2").start();
    }

    static void sleep(int time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class MyThread extends Thread {
        private String key;
        private String name;
        static Long start = System.currentTimeMillis();

        private Lock lock;

        public MyThread(String key, String name, Lock lock) {
            super(name);
            this.key = key;
            this.name = name;
            this.lock = lock;

        }

        @SneakyThrows
        @Override
        public void run() {
//            Lock lock = LockFactory.getLock(key);
            lock.lock();
            try {
                System.out.println(name + "开始执行 key:" + key);
                Long time = (System.currentTimeMillis() - start) / 1000;
                System.out.println(name + "开始释放 key:" + key + "时间:" + time);

            } finally {
                lock.unlock();
            }

        }
    }
}

如果不考虑从Map中释放key对应的锁,下面有更简单的实现.

 

public class JdkLockManager implements LockManager {

    private Map<Object, Lock> LOCK_MAP = Maps.newHashMap();

    private boolean fair;

    public JdkLockManager() {
        this.fair = false;
    }

    public JdkLockManager(boolean fair) {
        this.fair = fair;
    }

    @Override
    public Lock getLock(String key) {
        Assert.notNull(key, "key不能为空");
        Lock lock = LOCK_MAP.get(key);
        if (Objects.isNull(lock)) {
            synchronized (this) {
                lock = LOCK_MAP.get(key);
                if (Objects.isNull(lock)) {
                    lock = newLock();
                    //如果key已经无用就需要考虑从 LOCK_MAP 释放 key 对应的锁防止内存泄露
                    LOCK_MAP.put(key, lock);
                }
            }
        }
        return lock;
    }

    private Lock newLock() {
        return new ReentrantLock(fair);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值