分布式锁 并发 自旋 优化 二

自旋无界队列实现

上代码:
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.这里没有考虑死锁、锁超时等问题还有继续优化的空间,虽说不是常发生这样的问题,但发生一次也挺头疼

原文地址:https://www.cnblogs.com/zhaojj/p/7885826.html