AQS

浅出重入锁reentrantlock

下面简单演示一下 使用JDK的JUC并发包的reentrantlock

public class MyReentrantLock {
    // 锁是什么?
    // 锁是用来解决 多线程并发访问带来的资源竞争 引发的共享资源的安全性问题
    // 对一个共享资源加锁之后 如果一个线程获得了锁 那么其他线程不能访问这个资源
    public static Lock lock = new ReentrantLock();
    public static int count = 0;
    public static void incr() {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try{
            lock.lock();
            count+=2;
            // 在调用其他加该锁的方法 不需要再去争抢锁 只需要记录重入的次数
            decr();
            System.out.println("-->"+count);
        }finally {
            lock.unlock();
        }
    }

    public static void decr(){
        try {
            lock.lock();
            count--;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            new Thread(MyReentrantLock::incr).start();
        }
        Thread.sleep(2000);
        System.out.println(count);
    }
}

之前我们使用synchronized和volatile来保证结果得到1000,这次使用reentrantlock看起俩比较简洁
在这里插入图片描述
后面的各种锁的核心就是AQS,那到底什么是AQS,下面我们来一起看看源码
第一步看看lock()方法对应有两种实现方式:一种是公平锁 另一种是非公平锁
在这里插入图片描述
在这里插入图片描述
下面来分析一下非公平锁实现机制,在第一个线程进入compareAndSetState(期望值:0,更新成1),意思就是如果期望值是0那么就更新成1,然后后续的线程就跟期望值不相等,走下面的acquire(1);
在这里插入图片描述
这个互斥变量起着重要作用,0表示无锁状态,大于0表示有锁状态
在这里插入图片描述
在返回true之后,表示这个线程已经拿到这个锁,然后把当前线程保存到exclusiveOwnerThread;
后续的线程会返回false,继续acquire()
在这里插入图片描述
接下里分析acquire(),然后进入tryAcquire()里面看看
在这里插入图片描述
在这里插入图片描述
其他线程继续走acquireQueued(addWaiter(Node.EXCLUSIVE), arg),接下来就看里面的一个addWaiter(Node.EXCLUSIVE)
在这里插入图片描述
然后进入enq(node)看看,初始化Head 和 Tail结点都是null,然后把当前线程放进双向链表结点取代为空的Tail结点;
当下一个线程来就会走上图当中的enq(node)上面的逻辑,把下一个线程放入双向Node,这个算法比较简单刷过LeetCode基本一下就能看懂->双向链表

在这里插入图片描述
回到之前继续走acquireQueued(addWaiter(Node.EXCLUSIVE), arg),进入acquireQueued看看
在这里插入图片描述
然后进入shouldParkAfterFailedAcquire(p, node)里面看看,挂起自己之前,需要将前置节点的 ws 状态设置成 SIGNAL,告诉他:你释放锁的时候记得唤醒我

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    //  如果他的上一个节点的 ws 是 SIGNAL,他就需要阻塞。
    if (ws == Node.SIGNAL)
        // 阻塞
        return true;
    // 前任被取消。 跳过前任并重试。
    if (ws > 0) {
        do {
            // 将前任的前任 赋值给 当前的前任
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        // 将前任的前任的 next 赋值为 当前节点
        pred.next = node;
    } else { 
        // 如果没有取消 || 0 || CONDITION || PROPAGATE,那么就将前任的 ws 设置成 SIGNAL.
        // 为什么必须是 SIGNAL 呢?
        // 答:希望自己的上一个节点在释放锁的时候,通知自己(让自己获取锁)
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    // 重来
    return false;
}

!!!看JDK源码其实不是很难,这个比spring源码简单多了 简书的相关文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值