JAVA无锁编程--Atomic包的使用

本文介绍了Java中java.util.concurrent.atomic包的基本概念及其主要类的功能,包括原子更新基本类型、数组、引用及字段的方法和使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 简介

    Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。 
    在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。

  • 原子更新基本类型类

    用于通过原子的方式更新基本类型,Atomic包提供了以下三个类:

    • AtomicBoolean:原子更新布尔类型。
    • AtomicInteger:原子更新整型。
    • AtomicLong:原子更新长整型。 
      AtomicInteger的常用方法如下: 
      1、 int addAndGet(int delta) :以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果 
      2、boolean compareAndSet(int expect, int update) :如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。 
      3、int getAndIncrement():以原子方式将当前值加1,注意:这里返回的是自增前的值。 
      4、int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。
import java.util.concurrent.atomic.AtomicInteger;
 
public class AtomicIntegerTest {
 
static AtomicInteger ai = new AtomicInteger(1);
 
public static void main(String[] args) {
System.out.println(ai.getAndIncrement());
System.out.println(ai.get());
}
 
}

得到结果为:

1
2
  • 原子更新数组类
    通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:
  • AtomicIntegerArray:原子更新整型数组里的元素。
  • AtomicLongArray:原子更新长整型数组里的元素。
  • AtomicReferenceArray:原子更新引用类型数组里的元素。

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

  • int addAndGet(int i, int delta):以原子方式将输入值与数组中索引i的元素相加。
  • boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。
public class AtomicIntegerArrayTest {
 
static int[] value = new int[] { 1, 2 };
 
static AtomicIntegerArray ai = new AtomicIntegerArray(value);
 
public static void main(String[] args) {
ai.getAndSet(0, 3);
System.out.println(ai.get(0));
System.out.println(value[0]);
}
 
}

得到结果:

3
1
  • 原子更新引用类型

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

  • AtomicReference:原子更新引用类型。

  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
  • AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子的更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef, boolean initialMark)
public class AtomicReferenceTest {
 
public static AtomicReference<user> atomicUserRef = new AtomicReference</user><user>();
 
public static void main(String[] args) {
User user = new User("conan", 15);
atomicUserRef.set(user);
User updateUser = new User("Shinichi", 17);
atomicUserRef.compareAndSet(user, updateUser);
System.out.println(atomicUserRef.get().getName());
System.out.println(atomicUserRef.get().getOld());
}
 
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 int getOld() {
return old;
}
}
}

得到结果:

Shinichi
17
  • 原子更新字段类

    如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:

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

  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。(后面专门说) 
    原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater创建一个更新器。原子更新类的字段的必须使用public volatile修饰符。AtomicIntegerFieldUpdater的例子代码如下:
public class AtomicIntegerFieldUpdaterTest {
 
private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater
.newUpdater(User.class, "old");
 
public static void main(String[] args) {
User conan = new User("conan", 10);
System.out.println(a.getAndIncrement(conan));
System.out.println(a.get(conan));
}
 
public static class User {
private String name;
public volatile int old;
 
public User(String name, int old) {
this.name = name;
this.old = old;
}
 
public String getName() {
return name;
}
 
public int getOld() {
return old;
}
}
}

得到结果:10 11

  • ABA问题
    在运用CAS做Lock-Free操作中有一个经典的ABA问题: 
    线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题,例如下面的例子: 
     
    现有一个用单向链表实现的堆栈,栈顶为A,这时线程T1已经知道A.next为B,然后希望用CAS将栈顶替换为B: 
     
    此时轮到线程T1执行CAS操作,检测发现栈顶仍为A,所以CAS成功,栈顶变为B,但实际上B.next为null,所以此时的情况变为: 
     
    其中堆栈中只有B一个元素,C和D组成的链表不再存在于堆栈中,平白无故就把C、D丢掉了。 
    以上就是由于ABA问题带来的隐患,各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记,避免并发操作带来的问题,在Java中,AtomicStampedReference也实现了这个作用,它通过包装[E,Integer]的元组来对对象标记版本戳stamp,从而避免ABA问题。(这里我本人也不是特别的理解它具体怎么实现的)
<think>好的,我需要回答用户关于Java中无编程的问题,括概念、实现方法、示例代码和最佳实践。首先,我得回顾用户提供的引用内容,里面有关于自旋、CAS、ReentrantLock、StampedLock等信息,还有一些示例代码。 首先,无编程的定义应该基于用户提到的引用[2]和[3],即不使用的情况下实现线程同步,可能涉及CAS操作。需要解释清楚无和基于的区别,比如自旋其实还是用了CAS,但用户引用里提到自旋是轻量级,所以可能需要说明自旋是否属于无编程的范畴。根据引用[3],自旋虽然是基于CAS,但可能不算完全无,这里需要准确区分。 接下来,实现方法部分,用户引用中提到了CAS、FAA(Fetch-and-Add),还有Java中的原子类如AtomicInteger,以及StampedLock的乐观读。需要将这些整理出来,结合示例代码。比如引用[3]中的Go自旋示例,但用户需要Java的,所以可能需要转换一下,比如用AtomicBoolean或AtomicInteger来实现类似的自旋。 示例代码部分,用户提供的引用中有ReentrantLock和StampedLock的示例,但这些都是机制,而用户问的是无编程,所以应重点放在CAS和原子变量上。比如AtomicInteger的incrementAndGet方法,或者自定义的CAS循环。比如引用[4]提到的Java使用CAS和FAA,可能涉及AtomicIntegerFieldUpdater或类似工具。 最佳实践部分,需要结合引用中的内容,比如引用[4]提到的Yield或Sleep来减少CPU占用,但可能影响性能。需要建议在短任务中使用,避免长时间自旋,使用原子类代替,以及结合退避策略等。 另外,注意引用格式,每个引用末尾加上对应的标识,如[^1]、[^2]等。需要检查哪些引用对应哪些内容,比如引用[1]提到自旋的实现,引用[2]和[3]关于CAS和无,引用[4]关于Java编程的具体实现建议。 可能还需要澄清一些概念,比如无编程机制的区别,虽然自旋用了CAS,但严格来说可能不算无,而是乐观的一种。用户的问题是无编程,所以应该集中在完全不用的机制,比如CAS操作原子变量等。 在结构上,需要分点回答:概念、实现方法、示例代码、最佳实践,每个部分都要清晰,并引用相关内容。例如,在实现方法中,可以列出原子类、CAS循环、FAA等,每个点对应相应的引用。 示例代码部分可能需要用Java代码,比如使用AtomicInteger实现计数器,或者使用AtomicReference实现栈结构,如Treiber栈。这些例子能展示无编程的实际应用。 需要确保LaTeX格式正确,所有数学表达式用$...$,如CAS操作中的比较和交换条件。同时,避免使用Markdown格式,但用户允许在代码块中使用,所以示例代码需要用代码块裹。 最后,生成相关问题,可能需要涉及无编程的优缺点、性能比较、适用场景等,参考用户提供的示例问题结构。 总结步骤: 1. 定义无编程,引用[2][3] 2. 实现方法:CAS、原子类、FAA等,引用[2][3][4] 3. 示例代码:AtomicInteger,CAS循环,栈或队列的实现 4. 最佳实践:短操作、退避策略、避免ABA问题,引用[4] 5. 相关问题生成,比如优缺点、ABA问题解决、与机制比较等</think>### Java编程详解 #### 一、无编程概念 无编程(Lock-Free Programming)是**通过原子操作实现多线程同步的编程范式**,核心特点是不使用传统机制(如`synchronized`或`ReentrantLock`),而是依赖硬件支持的原子指令(如CAS)来保证线程安全[^2]。 严格的无算法需满足:至少有一个线程能够保证在有限步骤内完成操作,且不会因其他线程的阻塞而饿死[^3]。 --- #### 二、实现方法与示例 ##### 1. **原子类(Atomic Classes)** Java提供`java.util.concurrent.atomic`下的原子类,直接封装CAS操作: ```java AtomicInteger counter = new AtomicInteger(0); counter.incrementAndGet(); // 线程安全的自增 ``` ##### 2. **CAS循环(Compare-and-Swap Loop)** 手动实现无逻辑,例如无计数器: ```java public class LockFreeCounter { private AtomicInteger value = new AtomicInteger(0); public void increment() { int oldValue; do { oldValue = value.get(); } while (!value.compareAndSet(oldValue, oldValue + 1)); } } ``` ##### 3. **Fetch-and-Add(FAA)** 通过`AtomicInteger`的`getAndAdd`方法实现: ```java AtomicInteger sum = new AtomicInteger(0); sum.getAndAdd(10); // 原子性累加 ``` ##### 4. **乐观(Optimistic Locking)** 如`StampedLock`的乐观读模式: ```java StampedLock lock = new StampedLock(); long stamp = lock.tryOptimisticRead(); // 读取共享数据 if (!lock.validate(stamp)) { // 数据被修改,转为悲观读 stamp = lock.readLock(); } // 处理数据 lock.unlockRead(stamp); ``` --- #### 三、最佳实践 1. **适用短耗时操作**:CAS自旋适用于竞争时间短的场景(如计数器),长时间自旋会浪费CPU资源[^4]。 2. **退避策略**:在自旋循环中加入`Thread.yield()`或短暂休眠,减少CPU占用[^4]: ```java while (!atomicVar.compareAndSet(old, new)) { Thread.yield(); // 让出CPU时间片 } ``` 3. **优先使用原子类**:如`AtomicInteger`比手动CAS更高效且不易出错。 4. **处理ABA问题**:使用带版本号的原子类(如`AtomicStampedReference`): ```java AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0); int[] stampHolder = new int[1]; String current = ref.get(stampHolder); ref.compareAndSet(current, "B", stampHolder[0], stampHolder[0] + 1); ``` --- #### 四、应用场景 1. **高并发计数器**(如网站访问量统计)。 2. **无队列/栈**(如Disruptor框架)。 3. **实时系统**:避免带来的线程挂起风险。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值