Lock 锁底层实现

简介: Lock 锁底层实现

★ 1、讲讲 Lock 锁

是一个接口,有三个实现类,分别是常用的 可重入锁,读锁、写锁。常用的是可重入锁

加锁使用lock() 方法,解锁使用 unlock() 方法。Lock的底层是 AQS+CAS机制 实现。

Lock 常用子类 可重入锁ReentrantLock 有两种模式, 公平锁模式、非公平锁模式

公平锁模式非公平锁模式 的应用

  • 默认一般创建的是非公平锁,就是允许线程插队,而不是按先来后到顺序
  • 并发量高的,非公平可能会导致线程饿死 === 做中间件,比如rocketmq 就需要关注锁公平和不公平
  • mq 消息队列的应用,比如网易云多个用户的评论->mq->如果是非公平锁,那么导致线程饥饿,导致等待时间过长-不稳定
  • 解决:mq源码的queue包下有:
    RoundQueue(线程不安全),ConcurrentTreeMap(线程安全-put 方法使用了lock 加锁,且 lock = new ReentrantLock(true);)

可重入锁的意思是 对于同一线程可以重复去获取锁。应用场景--递归,例如文件夹遍历目录下的所有文件名。


★ 2、和synchronized 的使用区别/ 说说lock 和 synchronized 锁的区别

  • synchronized 是一个 关键字,使用C++实现的,没办法控制锁的开始、锁结束,也没办法中断线程的执行
  • 而 lock 是 java层面的实现可以获取锁的状态,开启锁,释放锁,通过设置可以中断线程的执行,更加灵活
  • 是否自动是否锁:synchronized 会自动是否锁,而 lock 需要手动调用unlock 方法释放,否则会死循环
lock.lock();//其他没有拿到锁的线程?阻塞 卡着不动
boolean res = lock.tryLock(1000, TimeUnit.MILLISECONDS);//一秒之后如果没有拿到锁,就返回false
lock.lockInterruptibly();//中断方法


★ 3、讲讲 trylock、lock方法

lock 锁设计上的核心成员:锁状态、锁拥有者、等待队列

源码方面:在 ReentrantLock 中 使用了关键成员是同步器AQS(源码中的Sync)


trylock方法:获取锁/是否锁成功

  • 锁的状态,0 代表未占用锁,大于0 则代表占用锁的次数。
  • 首先当前线程以CAS的方式,尝试将锁的状态从0修改成1,就是尝试获取锁。
  • 获取到了就把当前线程设置给AQS的属性exclusiveOwnerThread,也就是指明当前锁的拥有者是当前线程
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {//如果占用锁的是当前线程,则代表重入次数
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }


lock方法:加锁

  • 非公平锁模式,首先当前线程以CAS的方式,尝试将锁的状态从0修改成1,就是尝试获取锁。
  • 获取到了就把当前线程设置给AQS的属性exclusiveOwnerThread,也就是指明当前锁的拥有者是当前线程
  • 当前锁已经被占用,线程会进入等待队列,不断地抢锁,抢到锁直接从等待队列弹出,否则判断线程的状态是否需要挂起(阻塞),这里循环抢锁,不断调用了尝试获取锁的方法,也利用了CAS思想。
// 非公平锁模式 lock = new ReentrantLock();
final void lock() {
    // 首先以 CAS 的方式,尝试将state 从0修改成1 
   if (compareAndSetState(0, 1))
         setExclusiveOwnerThread(Thread.currentThread());
   else
         acquire(1);
}
// compareAndSetState(0, 1)--> CAS 机制
protected final boolean compareAndSetState(int expect, int update) {
 // See below for intrinsics setup to support this
  return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// acquire(1);--> CAS 机制
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//进入等待队列,继续不断尝试获取锁,直到抢到锁则弹出队列,否则判断线程的状态是否需要挂起
            selfInterrupt();
    }
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())//判断线程的状态是否需要挂起
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }



如果本文对你有帮助的话记得给一乐点个赞哦,感谢!

相关实践学习
RocketMQ一站式入门使用
从源码编译、部署broker、部署namesrv,使用java客户端首发消息等一站式入门RocketMQ。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
3月前
|
应用服务中间件 Linux 调度
锁和原子操作CAS的底层实现
锁和原子操作CAS的底层实现
19 0
|
6月前
|
Java 程序员 API
【Lock锁的使用与原理】
【Lock锁的使用与原理】
101 0
|
3月前
|
存储 安全 中间件
锁与原子操作CAS的底层实现
锁与原子操作CAS的底层实现
|
9月前
|
Java
JUC基础(三)—— Lock锁 及 AQS(2)
JUC基础(三)—— Lock锁 及 AQS
65 0
|
9月前
|
算法 调度
JUC基础(三)—— Lock锁 及 AQS(1)
JUC基础(三)—— Lock锁 及 AQS
83 0
|
11月前
|
API
【JUC基础】04. Lock锁
java.util.concurrent.locks为锁定和等待条件提供一个框架的接口和类,说白了就是锁所在的包。
5487 0
|
11月前
|
存储 安全 Java
synchronize偏向锁底层实现原理
无多线程竞争时,减少不必要的轻量级锁执行路径。大多数情况下,锁不仅不存在多线程竞争,而且总是由同一条线程去多次获得锁,为了让线程获得锁的性能代价更低而引入了偏向锁。
49 0
|
安全 Java 数据库
多线程之Lock显示锁
多线程之Lock显示锁
|
安全 Java 对象存储
浅谈synchronized锁原理
保证线程安全的一个重要手段就是通过加锁的形式实现,今天盘点一下Java中锁的八股文
129 0