当前位置:网站首页 > 技术博客 > 正文

juc并发编程



CAS: Compare and Swap,翻译成比较并交换。底层会执行函数CAS(V,E,N)

CAS有3个操作数,内存值V,旧的预期值E,要修改的新值N。当且仅当预期值E和内存值V相同时,将内存值V修改为N,否则什么都不做。

保证线程安全性的问题 原子性

(V,E,N)===乐观锁

V:全局共享变量 原子类中 value值 ===0

E:旧的预期值

N:修改的新值

CAS:无锁机制----自旋控制乐观锁

缺点:自旋会消耗的cpu的资源 cas 死循环 空转问题 cpu飙高

底层基于unsafe类实现

img

里面的 中的

  • this===atomicInteger 对象;
  • valueoffset ==值偏移量;
  • 1
 

属于Java并发包类库(原子类 cas)

比如我们所学习的:原子类同步之原子类(Atomic类)

  1. 原子更新基本类型类 cas
    • : 原子更新布尔类型。
    • : 原子更新整型。
    • : 原子更新长整型。
  2. 原子更新数组
    • : 原子更新整型数组里的元素。
    • : 原子更新长整型数组里的元素。
    • : 原子更新引用类型数组里的元素。
  3. 原子更新引用类型
    • : 原子更新引用类型。
    • : 原子更新引用类型的字段。
    • : 原子更新带有标记位的引用类型,可以使用构造方法更新一个布尔类型的标记位和引用类型。
  4. 原子更新字段类
    • : 原子更新整型的字段的更新器。
    • : 原子更新长整型字段的更新器。
    • : 原子更新带有版本号的引用类型。
  5. JDK8新增原子类
    • : 是一个用于累积双精度浮点数的原子类。它允许通过一个给定的组合函数来原子地累加值。这个类通常用于并行计算场景,例如并行流处理中的数值累积。
    • :类似于 , 用于累积长整型数值。它也支持通过一个组合函数原子地累加值,适用于需要在无锁情况下对长整型数据进行并行累加的场景。
    • : 是一个专门用于原子地累加双精度浮点数的类。与 不同, 默认使用加法作为组合函数,因此它简化了累加操作。它适用于需要快速并行累加双精度值的场景。
    • : 与 类似,但用于长整型数值。它提供了原子的累加操作,适合在高并发环境下快速累加长整型数据。

    Java 层面是由 Unsafe 类实现的

 

计数能够保证线程安全问题。

底层基于CAS实现

image-20240722214944560

类,全限定名是,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。

类是的,不允许继承。且构造函数是的

传递三个参数 【() (成员变量在内存中偏移量) ,1 】+1

=对象内存地址+内存偏移量

CAS前置知识

对象提供了非常底层的,操作内存、线程的方法, 对象不能直接调用,只能通过反射获得 已经移除

Cas底层 类实现-----java层面

Cas 能够保证线程安全性 数据的原子性------cpu硬件支撑

 
 

从对象的内存处开始,获得原始字节偏移量,用于存储实力对象的内存

在这里偏移量的意思就像我们 一个数组,数组的地址就是数组地一个元素的地址,假如数组地址是 ,第二个元素就是,其中就是偏移量。对应的对象的一个属性的偏移量就是其对象的地址开始增加,增加的数就是这个的偏移量。

对于这个值我们知道了,他是中的属性对应的偏移量,就是对象地址+ = 的地址

循环:获取当前 原子类的

img

底层如何实现:

  1. 创建了一个 类
  2. 先定义一个 类型值
  3. 底层都是基于类实现

源码:

 

E==V 则将N的值赋值给V;

3.1 手写AtomicInteger

 

3.2 手写Lock锁

 
 

3.3 CAS aba的问题

Cas主要检查 内存值V与旧的预值值=E是否一致,如果一致的情况下,则修改。

这时候会存在ABA的问题:如果将原来的值A,改为了B,B又改为了A 发现没有发生变化,实际上已经发生了变化,所以存在Aba问题。

解决办法:通过版本号码,对每个变量更新的版本号码做+1

解决aba问题是否大:概念产生冲突,但是不影响结果,换一种方式 通过版本号码方式。

img

3.4 AtomicMarkableReference

  • 第一个参数expectedReference:表示预期值。
  • 第二个参数newReference:表示要更新的值。
  • 第三个参数expectedStamp:表示预期的时间戳。
  • 第四个参数newStamp:表示要更新的时间戳。
 

CAS(Compare and Swap)或 Compare and Set 是一种无锁编程中常用的原子操作。它通常用于实现线程安全的数据结构和算法,而无需使用传统的互斥锁。CAS 操作的主要优点包括:

  1. 避免死锁
    CAS 减少了因传统锁机制而导致的死锁风险。在多线程环境中,如果线程获取锁的顺序不当,就可能产生死锁。CAS 操作不需要显式地获取和释放锁,从而降低了死锁的可能性。
  2. 提高并发性能
    CAS 操作可以并行执行,当多个线程尝试更新同一个数据时,其中一个线程会成功执行 CAS,而其他线程将失败并重试。这允许更多的线程同时进行工作,提高了系统的整体吞吐量。
  3. 低开销
    与锁相比,CAS 操作在硬件级别上实现,通常具有更低的系统开销。锁需要维护锁定状态、等待队列等,而 CAS 只需要简单的比较和交换指令。
  4. 非阻塞性
    使用 CAS 进行的更新是非阻塞的,这意味着即使一个线程在等待其 CAS 操作成功,它也不会阻止其他线程执行自己的 CAS 操作。这有助于保持系统的响应性和效率。
  5. 灵活性
    CAS 操作为设计高度优化的并发算法提供了基础。开发者可以直接控制并发行为,而不必依赖于更高层次的同步原语,如锁。
  6. 支持构建更复杂的同步结构
    利用 CAS 操作,可以构建更高级别的同步结构,如无锁队列、栈和其他数据结构,这些结构在高并发场景下表现优异。

然而,CAS 也并非没有缺点。例如,它可能导致 ABA 问题,在某些情况下需要额外的逻辑来解决。此外,过度的 CAS 失败和重试可能导致某些线程被饥饿,即无法获得更新数据的机会。在设计基于 CAS 的算法时,必须谨慎处理这些问题。

5.1 悲观锁

悲观锁比较悲观,当多个线程对同一行数据实现写的操作的时候,最终只会有一个线程才能写成功,只要谁能够对获取该到行锁则其他线程时不能够对该行数据做任何修写操作,且是阻塞状态。 比如 或者事务开启了事务但是没有提交事务。

存储引擎 InnoDB 事务

线程1 对操作 开启事务 没有提交和回滚事务;

线程2 对 操作 它会一直阻塞等待;

线程2 它会一直阻塞等待

  • 原因:线程1 开启了事务 没有提交和回滚事务 意味着其他的线程就不能够对该行数据做写的操作就会一直阻塞等待-----触发行锁机制
  • 目的:就是为了保证该行数据的线程安全性问题。

注意:中的为自动提交。

的值为,表示开启。

表示关闭

—关闭

  1. 用 BEGIN, ROLLBACK, COMMIT来实现
    • BEGIN 开始一个事务
    • ROLLBACK 事务回滚
    • COMMIT 事务确认
  2. 直接用 SET 来改变 MySQL 的自动提交模式:
  • SET AUTOCOMMIT=0 禁止自动提交
  • SET AUTOCOMMIT=1 开启自动提交
 

悲观锁比较悲观 —没有获取到锁 则会阻塞等待

如果升级为重量级锁(变成悲观锁) => C++ 对象

5.2 乐观锁

乐观锁比较乐观,通过预值或者版本号比较,如果不一致性的情况则通过循环控制修改,当前线程不会被阻塞,是乐观,效率比较高。

image-20240721132336183

版权声明


相关文章:

  • c语言编程题经典100例2025-03-01 07:01:02
  • pwn是什么意思2025-03-01 07:01:02
  • malloc 库文件2025-03-01 07:01:02
  • mysql8.0.23安装配置教程2025-03-01 07:01:02
  • 交叉验证的作用2025-03-01 07:01:02
  • 数组指针和指针数组的定义2025-03-01 07:01:02
  • java单元测试是什么2025-03-01 07:01:02
  • left join or2025-03-01 07:01:02
  • 分词助手2025-03-01 07:01:02
  • java协程原理2025-03-01 07:01:02