20250122面试鸭特训营第30天

更多特训营笔记详见个人主页【面试鸭特训营】专栏

250122

1. 10. 说说 AQS 吧?

AQS 的工作原理

  • AQS 通过维护一个共享状态 state 和一个先进先出 FIFO 的等待队列,来管理线程对共享资源的访问。
  • statevolatile 修饰,用于表示当前资源的状态。在独占锁中, state 为 0 表示已被占用,为 1 表示未被占用。
  • FIFO 等待队列底层是一个双向链表,节点包含线程的引用、等待状态及前驱和后继结点的指针。
  • 当线程无法获取同步器的资源时,会被加入到这个队列中。被唤醒之后线程会以 FIFO 顺序从队列中出队。

举例说明

  • 卫生间只有 5 个坑位,现在有 5 个人要上厕所。
  • 5 个人开始排队【FIFO 等待队列】进入坑位,状态值【state】都变成了 0 (已被占用)。
  • 第 6 个人来的时候,发现没有空的坑位了【state 都是 0】,只能在外面排队等候。
  • 当第 1 个人上完厕所之后,坑位的状态值变成了 1 (未被占用),此时第 6 个人进到坑位【从 FIFO 出队】。

常见实现类

AQS 常见的实现类有 ReentrantLockCountDownLatchSemaphore 等等。

2. 11. Synchronized 和 ReentrantLock 有什么区别?

Synchronized

特点
  • Synchronized 是 Java 内置的关键字,实现基本的同步机制,不支持超时、非公平、不可中断、不支持多条件。
  • Synchronized 修饰在方法或代码块上时,会对特定的对象或类加锁,从而确保同一时刻只有一个线程能执行加锁的代码块。
  • synchronized用于实现线程同步,确保同一时刻只有一个线程能访问资源。
  • synchronized可以用于修饰实例方法,静态方法,静态代码块。
  • synchronized能同时保证原子性、有序性和可见性。
  • synchronized具有可重入性,是可重入锁。
    • 可重入性是指:一个线程可以再次请求自己持有对象锁的临界资源。
    • 每获取一次锁,计数器加一,释放锁时,计数器减一。直到计数器为 0 时,锁才会真正释放。
  • synchronized 是非公平锁。
    • 公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到。
      • 优点:所有的线程都能得到资源,不会饿死在队列中。
      • 缺点:cpu多次唤醒队列中的阻塞线程导致开销较大。
    • 非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。
      • 优点:减少cpu对阻塞线程的唤醒次数,开销较少。
      • 缺点:可能会有部分线程长时间无法获取资源导致被饿死在队列。
锁的升级过程
  • 偏向锁
    • 在没有锁竞争的情况下,锁总是”偏向“于第一个获得它的线程。
    • 把当前锁偏向于某个线程,就是通过CAS机制来修改偏向锁的标记。
    • 若成功竞争到偏向锁,说明加锁成功,直接返回即可。
    • 若竞争偏向锁失败,说明当前已经有其他进程占用了偏向锁,就需要将锁升级到轻量级锁。
  • 轻量级锁(自旋锁)
    • 会通过多次自旋去重试竞争锁。
    • 在轻量级锁的状态下,竞争锁的线程会根据自适应的自旋次数,去尝试自旋占用锁资源。
    • 轻量级锁自旋次数过多,或获取锁失败的次数过多时,轻量级锁变成重量级锁。
  • 重量级锁
    • 如果线程无法通过轻量级锁,JVM 会将该锁升级为重量级锁。
    • 在重量级锁的状态下,没有竞争到锁的进程会被阻塞。

ReentrantLock

详解链接:https://tech.meituan.com/2019/12/05/aqs-theory-and-apply.html

  • ReentrantLock 是基于 AQS 实现的可重入锁,支持设置超时时间,可以避免死锁,比较灵活,并且支持公平锁和非公平锁、可中断、支持多条件判断。

在这里插入图片描述

表格对比

特性SynchronizedReentrantLock
获取锁自动获取显式获取,调用 lock()
释放锁自动释放显式释放,调用 unlock()
可重入性
公平性默认公平支持公平锁,可设置为公平或非公平
中断响应不响应中断支持中断响应(lockInterruptibly()
尝试锁不支持支持 tryLock()
锁的粒度方法或代码块级别方法或代码块级别,但更灵活
条件变量wait()/ notify()Condition
复杂度较简单提供更多控制,较复杂

3. 12. Java 中 volatile 关键字的作用是什么?

基本概念

  • volatile是Java提供的一种轻量级的同步机制,只能用于修饰变量。

可见性、有序性

  • 在多线程环境下,本身不具备可见性和有序性。
  • 不具备可见性是指:线程A对共享变量的修改,对于线程B来说是不可见的。
  • 不具备有序性是指:为了优化性能,编译器和处理器可能会对指令进行重排序,使执行顺序与代码顺序不完全相同。

如何保证可见性

  • volatile保证可见性的方案是
    • volatile 会对其修饰的变量的写操作添加Lock前缀指令,将缓存信息写入内存。
    • 缓存信息被写入内存后,会导致其他线程存储的内存地址的数据无效。
    • 线程在获取变量的值时,会检查自己现在缓存信息是否被修改。
    • 如果被修改,就从将自己的缓存信息更新为当前内存信息,然后再获取变量信息。

如何保证有序性

  • volatile 会对其修饰的变量添加读写内存屏障,进而实现禁止指令重排序,保证其原有的顺序。

原子性

  • 原子性是指:某些操作不可分割,要么全做,要么不做。

为何无法保证原子性

  • 被volatile修饰的变量不具有原子性,对于某个符合操作来说(如i++),多个线程同时进行i++,无法保证两个线程的原子性。i++实际上是先取i的值,再i+1,最后把新值写回i,可能线程A获取到i的值后还没有+1就已经阻塞了,此时线程B访问i,获取的还是原值,无法保证变量i的原子性。

volatile和synchronized的区别

  • volatile轻量级,开销较小,synchronized重量级,开销较大。
  • volatile能保证可见性和有序性,无法保证原子性,synchronized可以保证可见性有序性和原子性。
  • volatile不会造成线程的阻塞,synchronized有可能会造成线程的阻塞。
  • volatile适用于一个线程写,多个线程读,如状态标志,synchronized适用于临界区,如共享资源的读写。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值