博客
关于我
Java 并发编程_无锁并发
阅读量:115 次
发布时间:2019-02-26

本文共 2426 字,大约阅读时间需要 8 分钟。

无锁解决线程安全问题

在多线程环境下,线程安全是一个常见的问题。传统的解决方法是使用锁,这种方法通过让某一段代码在运行时独占处理,避免了并发访问带来的安全问题。然而,锁的使用可能会导致线程阻塞,尤其是在竞争激烈的情况下,会严重影响系统的性能。

CAS与volatile

在前面的示例中,我们看到 AtomicInteger 的实现并没有使用锁来保护共享变量的线程安全。这是因为 AtomicInteger 内部使用了 比较并交换(CAS) 原子操作。CAS 是一种乐观的锁机制,它假设其他线程不会在关键时刻修改共享变量。如果发现共享变量已经被修改,则会重试操作。这种机制不需要显式的锁,能够在多核环境下保证线程安全。

CAS的工作流程

当一个线程要修改共享变量时,首先会获取当前的值(pre),然后计算新的值(next)。随后,通过 CAS 操作,检查当前值是否等于 pre

  • 如果相等,说明没有其他线程在修改,立即将值设置为 next
  • 如果不相等,说明可能存在其他线程的修改操作,线程会重试。

volatile的作用

为了确保所有线程能够看到共享变量的最新值,必须使用 volatile 修饰。volatile 标志表示该变量的值会随着主存的变化而更新,所有线程都能访问到最新的值。然而,volatile 本身不能解决指令交错问题(无法保证原子性),因此需要结合 CAS 来实现线程安全。

CAS的优势

  • 无锁:没有显式的锁,线程不会被阻塞。
  • 无阻塞:线程可以在不占用锁的情况下执行操作,提高效率。
  • 乐观锁:假设其他线程不会修改共享变量,重试机制能够处理竞争情况。

原子整数

java.util.concurrent.atomic.AtomicInteger 提供了一个 thread-safe 的原子整数类型,适用于多线程环境下的计数器等场景。它支持原子操作,如自增、自减、加法和减法等,保证这些操作的原子性。

AtomicInteger的方法

  • getAndIncrement():获取并增加当前值。
  • incrementAndGet():增加当前值并返回新值。
  • getAndDecrement():获取并减少当前值。
  • decrementAndGet():减少当前值并返回旧值。
  • getAndAdd(long):获取并加上指定值。
  • addAndGet(long):加上指定值并返回新值。
  • getAndUpdate(BiFunction):通过函数更新当前值并返回旧值。
  • updateAndGet(BiFunction):执行函数并更新当前值,返回新值。

原子引用

对于需要原子操作的非基本类型(如 BigDecimal),可以使用 AtomicReference。它能够安全地管理对象引用,防止多线程环境下引用失效。

AtomicReference的方法

  • get():获取当前引用。
  • set(T):将引用设置为指定对象。
  • compareAndSet(Object, Object):比较当前引用和指定值,如果相等则将引用设置为指定值。

ABA问题与AtomicStampedReference

在并发环境下,可能会出现 ABA(Already Blocked, Already Accessing)问题,即多个线程同时尝试修改同一个共享变量。为了解决这一问题,AtomicStampedReference 在每次操作时记录变量的版本号,从而避免版本不一致的情况。

AtomicStampedReference的工作原理

  • 版本号:每次操作都需要一个版本号,用于跟踪变量的修改次数。
  • compareAndSet:比较当前引用和指定值,并对版本号进行校验,确保操作的原子性。

AtomicMarkableReference

如果只需要判断共享变量是否被修改过,可以使用 AtomicMarkableReference。它通过标记位来记录变量是否被修改,从而简化版本控制。

原子数组与原子字段更新器

除了单个原子操作,java.util.concurrent.atomic 包还提供了原子数组和原子字段更新器:

  • 原子数组:支持原子操作的数组类型,如 AtomicIntegerArray
  • 原子字段更新器:用于管理对象字段的原子更新,例如 AtomicReferenceFieldUpdater

LongAdder的原理

LongAdder 是一个高效的原子累加器,优化了 AtomicInteger 的实现,通过减少锁争用和缓存行的伪共享问题,提升了性能。

LongAdder的主要属性

  • cells:懒初始化的原子累加单元数组。
  • base:用于在无竞争情况下累加的基础值。
  • cellsBusy:用于标记当前单元是否被占用。

LongAdder的累加逻辑

  • 伪共享:通过添加空白数据(@sun.misc.Contended)打破缓存行的伪共享问题。
  • 扩展机制:在高竞争情况下动态扩展单元数组,减少锁的使用。

Unsafe对象

sun.misc.Unsafe 提供了底层的内存操作方法,主要用于实现高性能原子操作。它需要通过反射访问,通常用于高级的并发控制。

Unsafe的主要方法

  • compareAndSwap:实现 CAS 原子操作。
  • objectFieldOffset:获取对象字段的相对偏移量。
  • arrayOffset:获取数组元素的相对偏移量。

自己模拟实现原子整数

通过 sun.misc.Unsafe 模拟实现一个原子整数。关键在于使用 compareAndSwapInt 方法实现原子操作,并确保 volatile 修饰共享变量。

总结

通过合理使用 CASvolatileAtomicIntegerAtomicReference 等原子操作,可以在多线程环境下实现线程安全。选择合适的原子操作类型和结构,既能保证效率,又能避免并发问题。

转载地址:http://ssfz.baihongyu.com/

你可能感兴趣的文章
MySQL:判断逗号分隔的字符串中是否包含某个字符串
查看>>
Nacos在双击startup.cmd启动时提示:Unable to start embedded Tomcat
查看>>
Nacos安装教程(非常详细)从零基础入门到精通,看完这一篇就够了
查看>>
Nacos配置中心集群原理及源码分析
查看>>
nacos配置自动刷新源码解析
查看>>
Nacos集群搭建
查看>>
nacos集群搭建
查看>>
Navicat for MySQL 查看BLOB字段内容
查看>>
Neo4j电影关系图Cypher
查看>>
Neo4j的安装与使用
查看>>
Neo4j(2):环境搭建
查看>>
Neo私链
查看>>
nessus快速安装使用指南(非常详细)零基础入门到精通,收藏这一篇就够了
查看>>
Nessus漏洞扫描教程之配置Nessus
查看>>
Nest.js 6.0.0 正式版发布,基于 TypeScript 的 Node.js 框架
查看>>
NetApp凭借领先的混合云数据与服务把握数字化转型机遇
查看>>
NetBeans IDE8.0需要JDK1.7及以上版本
查看>>
netcat的端口转发功能的实现
查看>>
netfilter应用场景
查看>>
netlink2.6.32内核实现源码
查看>>