zoukankan      html  css  js  c++  java
  • 分布式锁 并发 自旋 优化 二

    自旋无界队列实现
    
    上代码:
    public class MCSLock {
    
        private static final Logger logger = LoggerManager.getLogger(MCSLock.class);
    
        private AtomicReference<QNode> tail;
    
        ThreadLocal<QNode> myNode;
    
        public MCSLock() {
            tail = new AtomicReference<QNode>(null);
            myNode = ThreadLocal.withInitial(()->new QNode());
        }
    
        public void lock() {
            QNode node = myNode.get();
            QNode preNode = tail.getAndSet(node);
            if (preNode!=null) {
                node.lock = true;
                preNode.next = node;
                while (node.lock) {
                    //也可以不休眠,自行考虑
                    try {
                        TimeUnit.MILLISECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        logger.error("锁休眠错误", e);
                    }
                }
            }
        }
    
        public void unlock() {
            QNode node = myNode.get();
            if (node.next==null) {
                // CAS操作,需要判断特殊情况是否新加入的节点但是next还没挂载上
                if (tail.compareAndSet(node, null)) {
                    // 没有新加入的节点,直接返回
                    return;
                }
                // 有新加入的节点,等待设置链关系
                while (node.next==null) {
    
                }
            }
            // 通知下一个节点获取锁
            node.next.lock = false;
            // 设置next节点为空
            node.next = null;
        }
    
        public static class QNode {
            volatile boolean lock;
            volatile QNode next;
        }
    }
    private static Map<String, MCSLock> lockMap = Collections.synchronizedMap(new HashMap<>());
    
    public static void releaseLock(String lockKey) {
            MCSLock lock = geMCSLock(lockKey);
            JedisClient.del(lockKey);
            lock.unlock();
        }
    
    
        public static void getLock(String lockKey) {
            MCSLock lock = geMCSLock(lockKey);
            lock.lock();
            boolean lockFlag = true;
            while (lockFlag) {//循环等待拿锁
                if (acquireLock(lockKey)) {
                    lockFlag = false;
                }
            }
        }
    
    
        public static MCSLock geMCSLock(String lockKey) {
            MCSLock lock = lockMap.get(lockKey);
            if (lock==null) {
                synchronized (lockMap) {
                    if (lock==null) {
                        lock = new MCSLock();
                        lockMap.put(lockKey, lock);
                    }
                    return lock;
                }
            }
            return lock;
        }

    说明:

    1.每个请求在获取锁时,会进队列,并在本节点上本地变量自旋

    2.在获取redis锁时先自旋,实际是能保证一个请求只会进行一次setnx,当然在多节点下,存在不是一次的情况,即在本节点获取到线程锁了,但是没获取到redis锁,这时候会进行redis锁的自旋,一个节点只会存在一个请求的redis自旋,实际性能是能接受的

    3.每个类型的业务一个锁,比如我这里就是一个活动一个本地锁

    4.这里没有考虑死锁、锁超时等问题还有继续优化的空间,虽说不是常发生这样的问题,但发生一次也挺头疼

  • 相关阅读:
    关于vue无法侦听数组及对象属性的变化的解决方案
    web js导出csv 文件(使用a标签)
    微信小程序-获取用户信息和openid,session_key,
    H5适配 iponex
    项目使用iconfont图标
    锚点链接,组件库中锚点实现
    前端的调试工具和node调试工具
    前端模块化 CommonJS AMD CMD ES6
    支付宝小程序
    个人创用 UA 列表
  • 原文地址:https://www.cnblogs.com/zhaojj/p/7885826.html
Copyright © 2011-2022 走看看