java 原子变量_Java原子变量

实现全局自增id最简单有效的方式是什么?java.util.concurrent.atomic包定义了一些常见类型的原子变量。这些原子变量为我们提供了一种操作单一变量无锁(lock-free)的线程安全(thread-safe)方式。实际上该包下面的类为我们提供了类似volatile变量的特性,同时还提供了诸如boolean compareAndSet(expectedValue, updateValue)的功能。不使用锁实现线程安全听起来似乎很不可思议,这其实是通过CPU的compare and swap指令实现的,由于硬件指令支持当然不需要加锁了。

先不去讨论这些细节,我们来看一下原子变量的用法。一个典型的用法是可以使用原子变量轻松实现全局自增id,就像下面这样:

// 线程安全的序列id生成器

class Sequencer {

private final AtomicLong sequenceNumber = new AtomicLong(0);

public long next() {

return sequenceNumber.getAndIncrement();

}

}

上述代码利用AtomicLong创建了一个Sequencer类,不断调用该类的next()方法就可以得到线程安全的自增id,用起来非常简单直观。下面我们给出每种原子变量类型的用法说明。

AtomicInteger and AtomicLong

AtomicInteger和AtomicLong分别代表原子类型的整型和长整型,这两个类提供十分相似的功能,仅仅是位宽不同。如上例所示,原子整型可用于多线程下全局自增id,除此之外还提供了原子比较-赋值等操作,诸如compareAndSet(expect, update), decrementAndGet(),getAndDecrement(),getAndSet(newValue)等等,更全面的接口描述可参考JDK文档。需要提醒的是这些函数都是通过原子CPU指令实现,执行效率较高。

原子整型看似跟普通整型(Integer, Long)类型相似,但不能使用原子整型替代普通整型,因为原子整型是可变的,而普通整型不可变。由于这个原因,使用原子整型作为Map的key并不是个好主意。

你可能会想当然的以为应该有AtomicFloat和AtomicDouble,遗憾的是类库里并没有这两个类型,AtomicByte和AtomicShort也没有。如果需要替代方案是使用AtomicInteger和AtomicLong。可通过Float.floatToRawIntBits(float)和Float.intBitsToFloat(int)将Float存储到AtomicInteger中,类似的Double类型也可以存储到AtomicLong中。

AtomicReference

AtomicReference用于存放一个可以原子更新的对象引用。该类包含get(), set(), compareAndSet(), getAndSet()等原子方法来获取和更新其代表的对象引用。

AtomicXXXArray

atomic包下面有三种原子数组:AtomicIntegerArray, AtomicLongArra, AtomicReferenceArray,分别代表整型、长整型和引用类型的原子数组。原子数组使得我们可以线程安全的方式去修改和访问数组里的单个元素。简单示例如下:

// 原子数组示例

AtomicLongArray longArray = new AtomicLongArray(10);// 创建长度为10的原子数组

longArray.set(1, 100);

long v = longArray.getAndIncrement(1);

AtomicReferenceArray referenceArray = new AtomicReferenceArray<>(16);

referenceArray.set(3, "love");

referenceArray.compareAndSet(3, "love", "you");

简单来说原子数组就是一种支持线程安全的数组,仍然具有数组“定长”的性质,如果访问元素超过了数组的长度,将会抛出IndexOutOfBoundsException。你可能已经想到了,可以使用线程安全的容器来避免容量不足,我们会在后续章节介绍。

什么是线程安全?

线程安全是指多线程访问是时,无论线程的调度策略是什么,程序能够正确的执行。导致线程不安全的一个原因是状态不一致,如果线程A修改了某个共享变量(比如给id++),而线程B没有及时知道,就会导致B在错误的状态上执行,结果的正确性也就无法保证。原子变量为我们提供了一种保证单个状态一致的简单方式,一个线程修改了原子变量,另外的线程立即就能看到,这比通过锁实现的方式效率要高;如果要同时保证多个变量状态一致,就只能使用锁了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java原子变量(Atomic Variables)是Java Concurrency API提供的一个特殊类型,用于保证在并发环境的线程安全读写操作。原子变量的主要作用是提供无锁、不可见的读写,使得对它们的更新操作是原子的,即不会被其他线程断。 Java提供了以下两种类型的原子变量: 1. `java.util.concurrent.atomic.Atomic*`包的类:如`AtomicInteger`, `AtomicLong`, `AtomicBoolean`等。这些类提供了基本类型的原子变量,如整数、布尔值等。使用这些类的实例,可以直接进行原子性的读写操作,如`get()`, `set()`, `incrementAndGet()`等方法。 ```java // 示例:声明和初始化一个 AtomicInteger AtomicInteger atomicInt = new AtomicInteger(0); ``` 2. `java.lang.Long`, `java.lang.Integer`, `java.lang.Boolean`等基本类型的 volatile 关键字修饰:如果一个基本类型变量被volatile修饰,那么其值的变化会立即被所有线程可见,但并不能保证原子性。通常情况下,除非你明确知道没有并发访问的需求,否则推荐使用原子变量类。 ```java // 示例:不推荐的volatile声明 volatile int volatileInt = 0; ``` 原子变量的使用通常配合`synchronized`、`ReentrantLock`等同步机制来确保在并发条件下的正确性。需要注意的是,虽然原子变量提供了高效的并发支持,但并不是所有情况都适合使用,过度使用可能会影响性能。在处理复杂的并发场景时,应当根据具体情况选择合适的并发工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值