ReentrantLock可重入锁实现指定粒度的锁控制

                                                  前提

        单体服务,该机制建立在单体服务的基础上实现的,如果是分布式服务,建议使用分布式锁。

 项目场景

           项目问题如下:积分兑换奖品,奖品有库存和兑换限制,每日奖品限量,且每家庭每个奖品只能兑换一次。如果在方法使用synchronized关键字,则所有奖品兑换都会变成串行,导致兑换逻辑排队,在并发兑换时严重影响用户体验。

        解决思路:使用ReentrantLock可重入锁来更加灵活的控制加锁粒度。

        新的问题:如果直接在逻辑中创建一个新的ReentrantLock并且尝试获取锁,无法指定锁对应的奖品类目,进而无法控制库存。

        解决思路:未ReentrantLock设计一个LockHolder,通过LockHolder来根据不同的key获取、使相同的key持有相同的ReentrantLock进而按照key的设计来细粒度的控制锁的粒度。

                                              核心代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

// 一、注入spring容器
@Component
public   class LockHolder {

    private Logger logger= LoggerFactory.getLogger(getClass());

    //二、内部私有化可重入读写锁
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    //三、从上面的可重入读写锁获取写锁
    private Lock writeLock = lock.writeLock();

    //四、从上面的可重入读写锁获取读锁
    private Lock readLock = lock.readLock();

    //五、可重入锁的容器

    private Map<String, ReentrantLock>lockMap=new ConcurrentHashMap() ;

    
    // 六、根据指定的key获取一把ReentrantLock,通过读写锁控制线程安全
    public ReentrantLock getLock(String key) {
        readLock.lock();
        try {
            ReentrantLock bizLock = lockMap.get(key);
            if (bizLock == null) {
                readLock.unlock();
                writeLock.lock();
                try {
                    readLock.lock();
                    bizLock = new ReentrantLock();
                    lockMap.put(key, bizLock);

                } finally {
                    writeLock.unlock();
                }
            }
            return bizLock;

        } finally {
            readLock.unlock();
        }
    }


   //七、定时清理不再使用的锁,释放内存
    public LockHolder() {
        new Thread(()->{

            while (true){
                writeLock.lock();

                try {
                    for (Map.Entry<String, ReentrantLock> entry : lockMap.entrySet()) {
                        String key = entry.getKey();
                        ReentrantLock lock = entry.getValue();
                        if(!lock.isHeldByCurrentThread()){
                            ReentrantLock remove = lockMap.remove(key);
                            logger.info("remove lock key={},lock={}",key,remove);
                        }
                    }
                } finally {
                    writeLock.unlock();
                }
                try {
//                    10 分钟
                    Thread.sleep(1000L*60*10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }

}

  使用示例(伪代码)        

 public void useLock() {

        ReentrantLock lock = lockHolder.getLock("指定key");
        try {
            lock.lock();
            //执行逻辑
        } finally {
            lock.unlock();
        }

    }

代码已经放在了gitee,项目地址:lockTool/src/main/java/com/zidong/lockToll/lock/LockHolder.java · DragonJiang/toolSet - Gitee.com

同时该项目包含一些其他实用的工具,欢迎使用。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值