Java面试题——为 什 么 说 Synchronized 是 一 个 悲 观 锁 ?乐 观 锁 的 实 现 原 理又 是 什 么 ? 什 么 是 CAS, 它 有 什 么 特 性 ?

  • a、Synchronized 显 然 是 一 个 悲 观 锁 , 因 为 它 的 并 发 策 略 是 悲 观 的 :不 管 是 否 会 产 生 竞 争 , 任 何 的 数 据操作 都 必 须 要 加 锁 、 用 户 态 ,核 心 态 转换 、 维 护 锁 计 数 器 和 检 查 是 否 有 被 阻 塞 的 线 程 需 要 被 唤 醒 等 操 作。
  • b、随 着 硬 件 指 令 集 的 发 展 , 我 们 可 以 使 用 基 于 冲 突 检 测 的 乐 观 并 发 策 略 。先 进 行 操 作 , 如 果 没 有 其他线 程 征 用 数 据 , 那 操 作 就 成 功 了 ;如 果 共 享 数 据 有 征 用 , 产 生 了 冲 突 , 那 就 再 进 行 其 他 的 补 偿 措施 。
  • 这 种乐 观 的 并 发 策 略 的 许 多 实 现 不 需 要 线 程 挂 起 , 所 以 被 称 为 非 阻 塞 同 步 。
  • c、乐 观 锁 的 核 心 算 法是 CAS( Compareand Swap, 比 较 并 交 换 ) , 它 涉及 到 三 个 操 作 数 : 内 存 值 、 预 期值 、 新 值 。 当 且 仅 当预 期 值 和 内 存 值 相等 时 才 将 内 存 值 修 改 为 新 值 。这 样 处 理 的 逻 辑 是 , 首 先 检 查 某 块内 存 的 值 是 否 跟之 前 我 读 取 时 的 一样 , 如 不 一 样 则 表 示 期 间 此 内 存 值 已 经 被 别 的 线 程 更 改 过 , 舍 弃 本次 操作 , 否 则说 明 期 间 没 有 其 他 线 程 对 此 内 存 值 操 作 , 可 以 把 新 值 设 置 给 此块 内 存 。
  • d、CAS 具 有 原 子 性 , 它 的 原 子 性由 CPU 硬 件 指 令 实 现 保 证 , 即 使 用JNI 调 用 Native 方 法 调 用 由 C++ 编 写的 硬 件 级 别 指 令 , JDK 中 提供 了Unsafe 类 执 行 这 些 操 作 。

概述

synchronized关键字最主要有以下3种应用方式:

  1. 修饰非静态方法(或者叫实例方法),调用该方法的当前实例充当锁;
  2. 修饰静态方法,类对象充当锁;
  3. 修饰代码块,要指定加锁对象。

1、乐观锁和悲观锁

乐观锁:乐观锁在处理一段代码时,它会乐观的认为读多写少,也就是并发执行的情况概率很低,代码都是串行执行的,他认为每次去拿数据时,别人都不会对数据进行修改,而只有当写数据时才会正式对数据上锁,它会先去获取一下版本号,看一下版本号有没有发生改变,如果发生改变,就会重复读–比较–写的操作(CAS操作),Java中的乐观锁一般都是通过CAS操作实现的。

悲观锁:悲观锁和乐观锁是完全相反的,它会认为读少写多,并发执行的概率很高,它会认为他在读取数据之后还会有线程对数据做出修改,所以他会在读取数据时就会上锁,这样后面的线程就拿不到锁对象了,从而进入阻塞状态,直到获取到锁,这样做会涉及到线程状态的来回切换,代价较高,性能较低,Java中Synchronized就是一个悲观锁。

Synchronized 是一个悲观锁的实现,因为它假设任何时候都有可能发生竞争,因此在执行同步代码块前会先获得锁,并且如果获取不到锁就会一直等待。这样可以确保在同一时间只有一个线程可以访问共享资源,从而保证线程安全。但是这种做法会带来一定的性能损失,因为大部分时间锁并没有被竞争,但是所有线程都需要等待。

相比之下,乐观锁的实现原理是假设竞争很少发生,因此在执行同步代码块前不会先获得锁,而是直接执行操作。在更新共享资源时,先读取资源的版本号,然后进行更新操作。如果其他线程在此期间修改了共享资源,那么版本号就会发生变化,此时更新操作会失败。此时,当前线程可以选择放弃操作,也可以重新尝试更新操作。乐观锁的实现方式通常会比悲观锁更加高效,因为大部分时间不需要等待锁的释放。

CAS(Compare And Swap)是一种基于乐观锁实现的原子操作。它的原理是先比较共享资源的当前值和期望值是否相等,如果相等就使用新值替换当前值。CAS 操作可以保证原子性,因为在执行操作期间,如果共享资源的值发生变化,CAS 操作会失败,此时就会重新尝试操作,直到成功为止。

CAS 操作具有以下特性:

  1. 原子性:CAS 操作是一种原子性操作,可以保证在同一时刻只有一个线程可以修改共享资源。
  2. 无锁操作:CAS 操作不需要加锁,因此不会引起线程阻塞和切换,效率比较高。
  3. ABA 问题:如果共享资源的值在操作过程中从 A 变成了 B,然后再从 B 变成了 A,那么 CAS 操作会错误地认为共享资源的值没有发生变化,从而引发潜在的问题。为了解决这个问题,可以使用版本号等机制来避免。

需要注意的是,CAS 操作虽然能够提高性能,但是也有一定的局限性,比如只能适用于单个共享变量的操作,不能适用于复合操作等场景。在使用 CAS 操作时需要根据实际场景进行取舍。

oldValue与newValue

oldValue:代表之前读到的资源对象的状态值

newValue:代表享要将资源对象的状态值更新后的值
“此时AB线程争抢着去修改资源对象的状态值,然后占用它”
设a抢到了时间片,

a 对比  对象状态值=0, oldValue=0

二者compare,结果相等,符合预期,则swap对象状态值为1

b 对比 对象状态值=1, oldValue=0。

二者compare,结果不等,抢占失败,放弃swap操作。
实际应用中b不放弃swap
实际应用,通常会让b进行自旋(不断第重试cas操作,且配置次数避免死循环)

内置锁synchronized

即synchronized关键字修饰的同步代码块,是一种互斥锁。

每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁。

用法1:类方法用synchronized关键字修饰,和synchronized(this)一样,获取的是当前类对象的互斥锁。

class ClassB{
    public synchronized void func1(){}
}

用法2:同步代码块,在任意对象上获取的是该对象的同步锁。

class ClassC{
    public Object lockObj = new Object(); 
    public void func1()
    {
       synchronized(lockObj){
}
    }  
}

参考文章: 

乐观锁(CAS)和悲观锁(synchronized)的详细介绍_傻鱼爱编程的博客-CSDN博客

悲观锁(Synchronized)和乐观锁(CAS)_synchronized是悲观锁吗_wyplj_sir的博客-CSDN博客

何谓悲观锁与乐观锁_林中静月下仙的博客-CSDN博客

常见的锁策略和synchronized实现原理_悲观锁为什么是重量级的_卑微小小羊的博客-CSDN博客

乐观锁与悲观锁讲解,CAS、synchronized、锁升级、ReentrantLock、AQS_synchronized是悲观锁吗_ZZYSY~的博客-CSDN博客 大聪明教你学Java | 深入浅出聊乐观锁与悲观锁(synchronized 悲观锁)_不肯过江东丶的博客-CSDN博客 Java_小弟季义钦的博客-CSDN博客

你可能感兴趣的:(java,开发语言)