博客
关于我
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/

你可能感兴趣的文章
NIFI同步MySql数据_到SqlServer_错误_驱动程序无法通过使用安全套接字层(SSL)加密与SQL Server_Navicat连接SqlServer---大数据之Nifi工作笔记0047
查看>>
Nifi同步过程中报错create_time字段找不到_实际目标表和源表中没有这个字段---大数据之Nifi工作笔记0066
查看>>
NIFI大数据进阶_FlowFile拓扑_对FlowFile内容和属性的修改删除添加_介绍和描述_以及实际操作---大数据之Nifi工作笔记0023
查看>>
NIFI大数据进阶_NIFI的模板和组的使用-介绍和实际操作_创建组_嵌套组_模板创建下载_导入---大数据之Nifi工作笔记0022
查看>>
NIFI大数据进阶_NIFI监控的强大功能介绍_处理器面板_进程组面板_summary监控_data_provenance事件源---大数据之Nifi工作笔记0025
查看>>
NIFI大数据进阶_内嵌ZK模式集群1_搭建过程说明---大数据之Nifi工作笔记0015
查看>>
NIFI大数据进阶_外部ZK模式集群1_实际操作搭建NIFI外部ZK模式集群---大数据之Nifi工作笔记0017
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_01_实际操作---大数据之Nifi工作笔记0029
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_02_实际操作_splitjson处理器_puthdfs处理器_querydatabasetable处理器---大数据之Nifi工作笔记0030
查看>>
NIFI大数据进阶_连接与关系_设置数据流负载均衡_设置背压_设置展现弯曲_介绍以及实际操作---大数据之Nifi工作笔记0027
查看>>
NIFI数据库同步_多表_特定表同时同步_实际操作_MySqlToMysql_可推广到其他数据库_Postgresql_Hbase_SqlServer等----大数据之Nifi工作笔记0053
查看>>
NIFI汉化_替换logo_二次开发_Idea编译NIFI最新源码_详细过程记录_全解析_Maven编译NIFI避坑指南001---大数据之Nifi工作笔记0068
查看>>
NIFI集群_内存溢出_CPU占用100%修复_GC overhead limit exceeded_NIFI: out of memory error ---大数据之Nifi工作笔记0017
查看>>
NIFI集群_队列Queue中数据无法清空_清除队列数据报错_无法删除queue_解决_集群中机器交替重启删除---大数据之Nifi工作笔记0061
查看>>
NIH发布包含10600张CT图像数据库 为AI算法测试铺路
查看>>
Nim教程【十二】
查看>>
Nim游戏
查看>>
NIO ByteBuffer实现原理
查看>>
Nio ByteBuffer组件读写指针切换原理与常用方法
查看>>
NIO Selector实现原理
查看>>