java中的原子操作

Java中一共有以下几种原子操作:

(1).原子更新基本类:

AtomicBoolean:原子更新布尔类型

AtomicInteger:原子更新整型

AtomicLong:原子更新长整型

它们常用的方法:

int addAndGet(int delta):以原子方式将输入的数值与实例中的值(AtomicInteger里的value),并返回结果:

boolean compareAndSet(int index,int update):如果输入的数值等于预期的值,则以原子方式將该值设置为输入的值:

int getAndIncrement():以原子的方式将当前的值加1,注意:这里返回的是自增前的值。

void lazySet(int newvalue):最终会设置成newvalue,使用lazySet设置后,可能导致其他线程在之后的一小段时间内还是可以读到旧值的。

intGetAndSet(int newValue):以原子的方式设置为newValue的值,并返回结果:

(2).原子更新数组
AtomicIntegerArray:原子更新整型数组里的元素。

AtominLongArray:原子更新长整型数组里的元素

AtomicReferenceArray:原子更新引用类型数组里的元素。

AtomicIntegerArray:类主要提供原子的方式更新数组里的整型,其常用方法如下:

int addAndGet(int i,int delta)以原子的方式将输入值和数组中的索引i的元素相加:

boolean compareAndSet(int i,int expect,int ipdate):如果当前值等于预期值,则以原子方式将数组的位置i的元素设置成update值:

(3).以原子更新引用类型

原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。

AtomicReference:原子更新引用类型

AtomcReferenceFieldUpdater:原子更新引用类型里的字段

AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef,boolean initialMark)

package AtomicArray;


import java.util.concurrent.atomic.AtomicReference;


public class AtomicReferenceTest {
public static AtomicReference<User>  atomicUserRef = new AtomicReference<User>();
static class User{
private String name;
private int old;
public User(String name,int old){
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOld() {
return old;
}
public void setOld(int old) {
this.old = old;
}

}
public static void main(String[] args) {
// TODO Auto-generated method stub
User user = new User("张三",12);
atomicUserRef.set(user);
User update = new User("shishi",12);
atomicUserRef.compareAndSet(user, update);
System.out.println(atomicUserRef.get().getName());

}


}

(4).原子更新字段类

如果需要原子地更新某类里面的字段,就需要使用原子更新字段类了,Atomic包里提供了以下3个类进行原子字段更新操作。

AtomicIntegerFieldUpdater:原子更新整型的字段的更新器

AtomicLongFieldUpdater:原子更新长整型字段的更新器

AtomicStampedReference:原子更新带有版本号的引用类型。

package AtomicArray;


import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;


public class AtomicIntegerFieldUpdaterTest {
//使用静态的方法newUpdater()创建一个更新器,必须指明要更新的类和字段。
private static AtomicIntegerFieldUpdater<User>  a = AtomicIntegerFieldUpdater.newUpdater(User.class, "old");
public void main(String[] args){
User conan = new User("张三",12);

System.out.println(a.getAndIncrement(conan));

System.out.println(a.get(conan));
}
static class User{
private String name;
public volatile int old;
public User(String name, int old) {
super();
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOld() {
return old;
}
public void setOld(int old) {
this.old = old;
}


}
}


<think>我们正在讨论Java中的原子操作及其实现原理。根据引用内容,原子操作是不可中断的一个或一系列操作。在多处理器环境中,实现原子操作需要特殊机制。Java通过`java.util.concurrent.atomic`包提供了一系列原子类,其底层实现依赖于处理器的CAS(Compare-and-Swap)指令,同时为了确保原子性,处理器提供了总线锁或缓存锁机制。 下面我将详细解答用户的问题: 1. **Java原子操作有哪些?** Java中的原子操作主要分为以下几类(参考之前分类): - 基本类型:`AtomicInteger`, `AtomicLong`, `AtomicBoolean` - 引用类型:`AtomicReference`, `AtomicStampedReference`, `AtomicMarkableReference` - 数组类型:`AtomicIntegerArray`, `AtomicLongArray`, `AtomicReferenceArray` - 字段更新器:`AtomicIntegerFieldUpdater`, `AtomicLongFieldUpdater`, `AtomicReferenceFieldUpdater` - 累加器(JDK8+):`LongAdder`, `DoubleAdder`, `LongAccumulator`, `DoubleAccumulator` 2. **实现原理** - **处理器级别**:通过总线锁或缓存锁保证原子性(引用[1][2]) - **总线锁**:使用处理器提供的一个`LOCK#`信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞,该处理器独占共享内存。 - **缓存锁**:通过缓存一致性协议(如MESI)来保证原子性。在修改内存地址时,如果该地址已经被缓存在处理器内部,则不会声明`LOCK#`信号,而是修改内部的内存地址,并利用缓存一致性协议保证原子性。 - **Java级别**:通过Unsafe类调用本地方法实现CAS操作(引用[4]) ```java public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x); ``` 其中,`o`是对象,`offset`是字段偏移量,`expected`是期望值,`x`是更新值。 **CAS操作原理**(引用[1][2][4]): CAS操作包含三个操作数:内存位置(V)、预期原值(A)和新值(B)。当且仅当内存位置的值等于预期原值时,处理器才会将该位置的值更新为新值。否则,处理器不做任何操作。无论哪种情况,都会返回该位置之前的值。CAS操作原子的,由一条处理器指令完成(如x86的`CMPXCHG`指令)。 **自旋CAS**:在Java原子类中,如果CAS操作失败,会通过循环不断尝试,直到成功为止。例如`AtomicInteger`的`getAndIncrement()`方法: ```java public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } ``` `Unsafe.getAndAddInt`的源码: ```java public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); // 读取当前值 } while (!compareAndSwapInt(o, offset, v, v + delta)); // 循环尝试直到CAS成功 return v; } ``` **ABA问题**:CAS操作可能会遇到ABA问题,即一个值原来是A,变成了B,又变回了A。此时使用CAS检查时会发现值没有变化,但实际上已经发生了变化。解决方案是使用版本号(`AtomicStampedReference`)或标记(`AtomicMarkableReference`)。 3. **高性能累加器LongAdder**(引用[3]) 在极高并发下,`AtomicLong`的自旋CAS操作可能因为大量线程竞争同一变量而导致性能下降。`LongAdder`通过分散热点来提高性能。其核心思想是:将单个变量拆分成多个变量(称为Cell),每个线程操作自己的Cell,最后将多个Cell的值累加起来得到总和。这样减少了竞争,提高了并发性能。 `LongAdder`的结构: - `base`:基础值,当没有竞争时直接操作该值 - `Cell[] cells`:当出现竞争时,使用Cell数组分散热点 `add`方法流程: 1. 尝试使用CAS更新`base`值 2. 如果失败,则尝试在Cell数组中更新自己线程对应的Cell(通过哈希算法确定位置) 3. 如果Cell数组未初始化或Cell未创建,则进行初始化或扩容 `sum`方法(获取总和): ```java public long sum() { Cell[] cs = cells; long sum = base; if (cs != null) { for (Cell c : cs) if (c != null) sum += c.value; } return sum; } ``` 注意:`sum`方法在没有并发更新时是准确的,但在并发更新时可能不是精确值(因为没有加锁)。 4. **原子类与锁的性能对比** 在低竞争环境下,原子类(如`AtomicInteger`)的性能优于锁(如`synchronized`或`ReentrantLock`),因为避免了线程上下文切换。在高竞争环境下,`LongAdder`的性能显著优于`AtomicLong`,因为它分散了竞争。 5. **适用场景** - 计数器:低竞争用`AtomicInteger`,高竞争用`LongAdder` - 状态标志:`AtomicBoolean` - 对象引用更新:无ABA问题用`AtomicReference`,有ABA问题用`AtomicStampedReference` - 数组元素更新:`AtomicIntegerArray`等 - 对象字段更新:`AtomicIntegerFieldUpdater`(适用于已存在的对象,无需创建新的原子对象) 6. **限制** 原子类只能保证单个变量的原子性,对于多个变量的操作(复合操作)需要借助锁或循环CAS(例如使用`AtomicReference`来保证多个变量的原子更新)。 根据以上内容,整理回答如下:</think>### Java原子操作详解:实现原理与原子类机制 #### 一、原子操作的核心定义 原子操作指**不可被中断的一个或一系列操作**,必须满足: 1. **原子性**:操作要么完全执行,要么完全不执行 2. **可见性**:操作结果立即对所有线程可见 3. **有序性**:禁止指令重排序优化 #### 二、处理器级原子操作实现原理 原子操作的硬件基础由CPU提供: 1. **总线锁机制**(解决原子性问题) - 当处理器发出`LOCK#`信号时,独占内存总线[^1] - 其他处理器被阻塞,无法访问内存 - 示例:执行`i++`时锁定总线,确保读-改-写操作原子性 ```assembly LOCK CMPXCHG ; x86原子指令 ``` 2. **缓存锁机制**(性能优化) - 基于**缓存一致性协议**(如MESI) - 只锁定特定内存地址,而非整个总线 - 当检测到共享变量被修改时,使其他处理器的缓存失效[^2] #### 三、Java原子类实现原理 Java通过`Unsafe`类调用本地CAS指令: ```java // AtomicInteger核心源码 public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } ``` - **CAS操作流程**: 1. 读取当前值$V$ 2. 计算更新值$V' = V + \Delta$ 3. 提交时检查$V$是否被修改 4. 未修改则更新,否则重试(自旋) - **内存屏障保证可见性**: ```java public final int get() { return value; // volatile读屏障 } public final void set(int newValue) { value = newValue; // volatile写屏障 } ``` #### 四、Java原子类分类及特性 | **类型** | **实现类** | **CAS指令** | **适用场景** | |----------------|-------------------------------|-----------------------|--------------------------| | 基础类型 | `AtomicInteger`, `AtomicLong` | `compareAndSwapInt` | 计数器、状态标志 | | 引用类型 | `AtomicReference` | `compareAndSwapObject`| 对象引用更新 | | 数组类型 | `AtomicIntegerArray` | 元素级CAS | 并发数组操作 | | 版本控制 | `AtomicStampedReference` | 带版本号CAS | 解决ABA问题[^4] | | 高性能累加器 | `LongAdder` | 分段CAS | 高并发统计[^3] | #### 五、关键优化技术解析 1. **分段CAS(LongAdder)** ```mermaid graph LR A[add(x)调用] --> B{尝试更新base} B -->|成功| C[返回] B -->|失败| D[初始化Cell数组] D --> E[哈希到特定Cell] E --> F[更新Cell值] ``` - **设计优势**: - 基础值$base$ + Cell数组$cells[0..n-1]$ - 总数值$sum = base + \sum_{i=0}^{n-1} cells[i]$ - 并发写分散到不同Cell,减少冲突[^3] 2. **ABA问题解决方案** - **问题本质**:值A→B→A的变化无法被检测 - **版本控制**: ```java // AtomicStampedReference实现 public boolean compareAndSet(V expected, V newValue, int expectedStamp, int newStamp) { Pair<V> current = pair; return (current.reference == expected && current.stamp == expectedStamp) && ((newValue == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newValue, newStamp))); } ``` #### 六、原子操作 vs 锁机制 | **特性** | **原子类** | **synchronized** | |----------------|-----------------|------------------| | 实现机制 | CAS自旋 | 管程(Monitor) | | 线程阻塞 | 非阻塞 | 阻塞 | | 竞争处理 | 乐观锁 | 悲观锁 | | 高并发性能 | 优(无上下文切换)| 差 | | 复合操作支持 | 需手动循环实现 | 天然支持 | > **最佳实践**: > - 低竞争场景:优先选用`AtomicInteger`等基础原子类 > - 高并发计数:使用`LongAdder`(如QPS统计) > - 对象引用更新:带版本号的`AtomicStampedReference` > - 复合操作:使用锁或`compareAndSet`循环实现 --- ### 相关问题 1. CAS操作在高并发场景下可能出现哪些性能问题?如何优化? 2. `LongAdder`的`sum()`方法为什么在并发环境下可能不准确? 3. 如何用`AtomicReference`实现无锁栈(Lock-Free Stack)? 4. 原子类和`volatile`关键字在内存可见性保证上有何异同? 5. ABA问题在实际系统中会导致哪些具体风险?请举例说明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值