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

你可能感兴趣的文章
NO32 网络层次及OSI7层模型--TCP三次握手四次断开--子网划分
查看>>
NoClassDefFoundError: org/springframework/boot/context/properties/ConfigurationBeanFactoryMetadata
查看>>
Node JS: < 一> 初识Node JS
查看>>
Node-RED中使用JSON数据建立web网站
查看>>
Node-RED中使用json节点解析JSON数据
查看>>
Node-RED中使用node-random节点来实现随机数在折线图中显示
查看>>
Node-RED中使用node-red-browser-utils节点实现选择Windows操作系统中的文件并实现图片预览
查看>>
Node-RED中使用node-red-contrib-image-output节点实现图片预览
查看>>
Node-RED中使用node-red-node-ui-iframe节点实现内嵌iframe访问其他网站的效果
查看>>
Node-RED中使用Notification元件显示警告讯息框(温度过高提示)
查看>>
Node-RED中实现HTML表单提交和获取提交的内容
查看>>
Node-RED中通过node-red-ui-webcam节点实现访问摄像头并截取照片预览
查看>>
Node.js 8 中的 util.promisify的详解
查看>>
Node.js 函数是什么样的?
查看>>
Node.js 历史
查看>>
Node.js 在个推的微服务实践:基于容器的一站式命令行工具链
查看>>
Node.js 实现类似于.php,.jsp的服务器页面技术,自动路由
查看>>
Node.js 异步模式浅析
查看>>
node.js 怎么新建一个站点端口
查看>>
Node.js 文件系统的各种用法和常见场景
查看>>