CLH锁原理

CLH锁其实是为了优化自旋锁的缺点:

所有自旋锁都在一个内存地址上自旋,持有锁的线程释放锁后,会引发惊群效应,而且会造成个别线程一只拿不到锁,处在饥饿状态,CLH锁通过队列将所有线程排队,避免惊群效应,也保证所有线程都能执行。

简单的自旋锁实现:

public class SpinLock {

    private AtomicReference<Thread> owner = new AtomicReference<Thread>();


    public boolean lock() throws InterruptedException {
        Thread currThread = Thread.currentThread();

        while (!owner.compareAndSet(null,currThread)){
            System.out.println("本次未获得锁,继续自旋获取锁,thread name:"+currThread.getName());
            Thread.sleep(1000);
        }
        System.out.println("获得锁成功,thread name:"+currThread.getName());
        return true;
    }

    public boolean unlock() {
        Thread currThread = Thread.currentThread();
        if (owner.compareAndSet(currThread,null)){
            System.out.println("释放锁成功,thread name:"+currThread.getName());
            return true;
        }else {
            return false;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SpinLock lock = new SpinLock();
        Thread t1 = new Thread(()->{
            try {
                if(lock.lock()){
                    System.out.println("获得锁,任务开始, t:"+ Thread.currentThread().getName());
                    Thread.sleep(1000);
                    lock.unlock();
                    System.out.println("任务结束,释放锁 t:"+ Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });


        Thread t2 = new Thread(()->{
            try {
                if(lock.lock()){
                    System.out.println("Get the SpinLock, t:"+ Thread.currentThread().getName());
                    Thread.sleep(1000);
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        t2.start();

    }

}

CLH原理:

将线程包装为CLH节点,locked代表线程状态,false表示释放锁或未持有锁,true表示持有锁或正在排队等待锁。

    @Data
    private static class Node{
        volatile boolean locked = false;
        private Thread curThread;
        private Node preNode;
    }

获得锁:

1.设置一个尾节点tail,初始为一个空节点,节点状态为false;

2.新加入的线程直接到队尾排队,这一步通过CAS保证成功;

3.将前一个节点set进当前节点的属性;

4.自旋监听前一个节点的状态是否为释放锁;

5.获得锁;

释放锁:

1.将当前节点的状态设置为false,表示锁已释放

2.清空前一个节点,直接设置为null(help GC)

public class CLHLock {

    @Data
    private static class Node{
        volatile boolean locked = false;
        private Thread curThread;
        private Node preNode;
    }

    AtomicReference<Node> tail =new  AtomicReference(new Node());

    ThreadLocal<Node> currNode = new ThreadLocal();

    public boolean lock(Thread t){
        Node currentNode = new Node();
        currentNode.setCurThread(t);
        Node preNode = tail.getAndSet(currentNode);
        currentNode.setPreNode(preNode);
        currentNode.setLocked(true);
        while (currentNode.getPreNode().locked);
        System.out.println("线程"+t.getName()+"获得锁");
        currNode.set(currentNode);
        return true;
    }

    public boolean unlock(Thread t){
        Node currentNode = currNode.get();
        if(null==currentNode){
            return false;
        }
        currentNode.setLocked(false);
        System.out.println("线程"+t.getName()+"释放锁成功");
        currentNode.setPreNode(null);
        //用完要释放,避免内存泄露
        currNode.remove();
        return true;
    }

    private static int counter = 0;
    public static void main(String[] args) {

        CLHLock clhLock = new CLHLock();

        Runnable task = new Runnable() {
            @Override
            public void run() {
                Thread t = Thread.currentThread();
                if (clhLock.lock(t)) {
                    counter++;
                    System.out.println("线程" + t.getName() + "执行加法成功,counter:" + counter);
                    clhLock.unlock(t);
                }
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        Thread t3 = new Thread(task);
        Thread t4 = new Thread(task);
        Thread t5 = new Thread(task);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

AQS论文地址 https://gee.cs.oswego.edu/dl/papers/aqs.pdf

AQS源码分析https://javadoop.com/post/AbstractQueuedSynchronizer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值