zoukankan      html  css  js  c++  java
  • 我理解的CLH


    学而时习之,不亦说乎!

                                 --《论语》

    原创,转载请附原文链接,谢谢。

    CLH

    思路

    • 保持时序的锁基本思路就是将等待获取锁的线程放入集合,锁释放后,等待线程之一获取到锁。

    问题

    • 如何排队?

      • CLH使用反向链表的形式进行排队。也就是后继节点主动询问,而不是前继节点主动通知。
    • 排队是否公平?

      • CLH是公平锁。即后申请获取锁的排在队列末尾。
    • 如何唤醒?

      • CLH通过每个线程自旋。每个等待线程通过不断自旋前继节点状态判断是否能获取到锁。

    实现

    • CLH排队使用公平方式,锁内部需要保存【尾节点】的引用,每次排队线程加入到队列最末尾。
    • CLH锁使用反向链表方式进行排队,那么每个线程就需要维护【自己的状态】和保持一个【前向节点的引用】。
    • CLH使用一个【boolean值】表示当前线程获取锁状态。false表示当前线程释放锁;true表示当前线程等待获取锁或已经获取到锁。

    代码

    Lock.java

     
     
     
    xxxxxxxxxx
     
     
     
     
    package com.zby.clh;
    public interface Lock {
        void lock();
        void unlock();
    }
     

    CLHLock.java

     
     
     
    xxxxxxxxxx
     
     
     
     
    package com.zby.clh;
    import java.util.concurrent.atomic.AtomicReference;
    public class CLHLock implements Lock {
        private final AtomicReference<QNode> tail;
        private final ThreadLocal<QNode> myPred;
        private final ThreadLocal<QNode> myNode;
        public CLHLock() {
            tail = new AtomicReference<QNode>(new QNode());
            myNode = new ThreadLocal<QNode>() {
                @Override
                protected QNode initialValue() {
                    return new QNode();
                }
            };
            myPred = new ThreadLocal<QNode>() {
                @Override
                protected QNode initialValue() {
                    return null;
                }
            };
        }
        @Override
        public void lock() {
            QNode qnode = myNode.get();
            qnode.locked = true;
            QNode pred = tail.getAndSet(qnode);
            myPred.set(pred);
            while (pred.locked) {
            }
        }
        @Override
        public void unlock() {
            QNode qnode = myNode.get();
            qnode.locked = false;
            // myNode.set(myPred.get());这一行是否有必要???
        }
        private static class QNode {
            public volatile boolean locked;
        }
    }
     

    测试

     
     
     
    xxxxxxxxxx
     
     
     
     
    package com.zby.clh;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    public class CLHMain {
        private Lock lock = new CLHLock();
        public void say(String msg) {
            System.out.println(Thread.currentThread().getName() + " : 尝试获取锁");
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " : 获取到锁");
            try {
                System.out.println(Thread.currentThread().getName() + msg + "?");
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + msg + "!");
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + msg + ".");
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + " : 释放锁");
            }
        }
        public static void main(String[] args) throws Exception {
            String msg = "Hello,World";
            CLHMain clhMain = new CLHMain();
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < 5; i++) {
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        clhMain.say(msg);
                    }
                });
            }
            executorService.awaitTermination(20, TimeUnit.SECONDS);
        }
    }
     

    结果

     
     
     
    xxxxxxxxxx
     
     
     
     
    pool-1-thread-1 : 尝试获取锁
    pool-1-thread-2 : 尝试获取锁
    pool-1-thread-1 : 获取到锁
    pool-1-thread-1Hello,World?
    pool-1-thread-3 : 尝试获取锁
    pool-1-thread-4 : 尝试获取锁
    pool-1-thread-5 : 尝试获取锁
    pool-1-thread-1Hello,World!
    pool-1-thread-1Hello,World.
    pool-1-thread-1 : 释放锁
    pool-1-thread-2 : 获取到锁
    pool-1-thread-2Hello,World?
    pool-1-thread-2Hello,World!
    pool-1-thread-2Hello,World.
    pool-1-thread-2 : 释放锁
    pool-1-thread-3 : 获取到锁
    pool-1-thread-3Hello,World?
    pool-1-thread-3Hello,World!
    pool-1-thread-3Hello,World.
    pool-1-thread-3 : 释放锁
    pool-1-thread-4 : 获取到锁
    pool-1-thread-4Hello,World?
    pool-1-thread-4Hello,World!
    pool-1-thread-4Hello,World.
    pool-1-thread-4 : 释放锁
    pool-1-thread-5 : 获取到锁
    pool-1-thread-5Hello,World?
    pool-1-thread-5Hello,World!
    pool-1-thread-5Hello,World.
    pool-1-thread-5 : 释放锁
     
  • 相关阅读:
    把CentOS改成中文
    String,StringBuffer,StringBuilder三者性能对比
    在Linux上部署安装C/C++开发环境
    Kali Linux安装ssh服务
    Kali VirtualBox安装增强功能
    CentOS安装docker
    CentOS安装jdk11
    Java基本数据类型
    奥卡姆剃刀定律在Java代码中的体现——记一次LeetCode刷题心得
    Java 实现简易登录注册功能
  • 原文地址:https://www.cnblogs.com/zby9527/p/10815563.html
Copyright © 2011-2022 走看看