CLH Lock & MCS Lock

来源:互联网 发布:优化一个关键词多少钱 编辑:程序博客网 时间:2024/06/13 08:50

共性: 都是公平锁,先进先出,通过自旋轮询状态

区别: CLH锁持有前个节点状态并轮询,MCS持有后一个节点状态并轮询。


0,节点类 QNode

package com.test.learn;public class LockNode {    /**     * 标志锁的状态     */    volatile boolean locked = false;    /**     * 用于保存后个节点     */    volatile LockNode next = null;}



1,CLHLock

package com.test.learn;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicReference;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;public class CLHLock implements Lock {    // 提供CAS方式修改地址引用    private AtomicReference<LockNode> tail;    // 保存前一个节点引用    private ThreadLocal<LockNode> myPre;    // 当前节点    private ThreadLocal<LockNode> myNode;    public CLHLock() {        tail = new AtomicReference<>(new LockNode());        myNode = ThreadLocal.withInitial(LockNode::new);        myPre = ThreadLocal.withInitial(() -> null);    }    @Override    public void lock() {        // L1.将自身节点状态标志为true(已上锁)        LockNode node = myNode.get();        node.locked = true;        // L2.获取前个节点        LockNode pre = tail.getAndSet(node);        myPre.set(pre);        while (pre.locked) {            // L3.自旋等待前个节点释放锁,即前一个节点执行unLock(U2处)        }    }    @Override    public void unlock() {        // 获取当前节点        LockNode qnode = myNode.get();        // 通过状态标志为锁已释放        qnode.locked = false;        // 回收前驱节点        myNode.set(myPre.get());    }    @Override    public void lockInterruptibly() throws InterruptedException {        // TODO    }    @Override    public boolean tryLock() {        // TODO        return false;    }    @Override    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {        // TODO        return false;    }    @Override    public Condition newCondition() {        // TODO        return null;    }}

2,MCSLock

package com.test.learn;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicReference;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;public class MCSLock implements Lock {    // 提供CAS方式修改地址引用    private AtomicReference<LockNode> tail;    // 保存当前节点    private ThreadLocal<LockNode> myNode;    public MCSLock() {        // tail和CLH有区别,初始值为null        tail = new AtomicReference<>();        myNode = ThreadLocal.withInitial(LockNode::new);    }    @Override    public void lock() {        LockNode qnode = myNode.get();        // L1,获取前节点        LockNode pre = tail.getAndSet(qnode);        // L2.不存在前一个节点则直接获取锁了,否则自旋前节点的状态        if (null != pre) {            qnode.locked = true;            // L3 设置后续节点            pre.next = qnode;            while (qnode.locked) {                // 自旋前节点的状态            }        }    }    @Override    public void unlock() {        LockNode qnode = myNode.get();        if (qnode.next == null) {            // 为什么需要CAS判断是否更新成功?因为多线程下可能新增了新的节点。            if (tail.compareAndSet(qnode, null)) {                return;            }            // 循环到等到next有值后退出,也即后一个节点执行L3之后            while (qnode.next == null) {            }        }        // 解锁时候主动更新后节点状态(和CLH的主要差距点)        qnode.next.locked = false;        qnode.next = null;    }    @Override    public void lockInterruptibly() throws InterruptedException {        // TODO    }    @Override    public boolean tryLock() {        // TODO        return false;    }    @Override    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {        // TODO        return false;    }    @Override    public Condition newCondition() {        // TODO        return null;    }}

3, 使用

package com.test.learn;import java.util.concurrent.CountDownLatch;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.locks.Lock;public class LockMain {    private static final int THEAD_CNT = 10;    private static Lock getLock() {        // return new MCSLock();        return new CLHLock();    }    public static void main(String[] args) throws Exception {        CyclicBarrier cb = new CyclicBarrier(THEAD_CNT);        CountDownLatch latch = new CountDownLatch(THEAD_CNT);        Lock lock = getLock();        for (int i = 0; i < THEAD_CNT; i++) {            Thread th = new Thread(() -> {                try {                    cb.await();                    System.out.println(Thread.currentThread().getName());                    lock.lock();                    System.out.println(Thread.currentThread().getName() + " Got the Lock!");                    Thread.sleep(100);                    lock.unlock();                    System.out.println(Thread.currentThread().getName() + " Released the Lock!");                    latch.countDown();                } catch (Exception e) {                    e.printStackTrace();                }            }, "Thread:" + i);            th.start();        }        long start = System.currentTimeMillis();        latch.await();        System.out.println("Duration:" + (System.currentTimeMillis() - start));    }}