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

你可能感兴趣的文章
node编译程序内存溢出
查看>>
Node读取并输出txt文件内容
查看>>
node防xss攻击插件
查看>>
noi 1996 登山
查看>>
noi 7827 质数的和与积
查看>>
NOI-1.3-11-计算浮点数相除的余数
查看>>
NOI2010 海拔(平面图最大流)
查看>>
NOIp2005 过河
查看>>
NOIP2011T1 数字反转
查看>>
NOIP2014 提高组 Day2——寻找道路
查看>>
noip借教室 题解
查看>>
NOIP模拟测试19
查看>>
NOIp模拟赛二十九
查看>>
Vue3+element plus+sortablejs实现table列表拖拽
查看>>
Nokia5233手机和我装的几个symbian V5手机软件
查看>>
non linear processor
查看>>
Non-final field ‘code‘ in enum StateEnum‘
查看>>
none 和 host 网络的适用场景 - 每天5分钟玩转 Docker 容器技术(31)
查看>>
None还可以是函数定义可选参数的一个默认值,设置成默认值时实参在调用该函数时可以不输入与None绑定的元素...
查看>>
NoNodeAvailableException None of the configured nodes are available异常
查看>>