Home | 简体中文 | 繁体中文 | 杂文 | Github | 知乎专栏 | Facebook | Linkedin | Youtube | 打赏(Donations) | About
知乎专栏

10.3. 原子变量

10.3.1. AtomicInteger / AtomicLong

		
AtomicInteger sequence = new AtomicInteger(1);
sequence.getAndIncrement(); // 加1操作
		
			

10.3.2. AtomicBoolean

		
package cn.aigcsst.conference.config;

import java.util.concurrent.atomic.AtomicBoolean;
		
public class AtomicService {
    private AtomicBoolean isRunning = new AtomicBoolean(false);

    // 开启服务  
    public void start() {
        isRunning.set(true);
        System.out.println("Service started.");
    }

    // 关闭服务  
    public void stop() {
        isRunning.set(false);
        System.out.println("Service stopped.");
    }

    // 检查服务是否正在运行  
    public boolean isRunning() {
        return isRunning.get();
    }
    
} 		
		
			

10.3.3. AtomicReference

		
private static AtomicReference<BankCard> bankCardRef = new AtomicReference<>(new BankCard("Neo",100));		
		
			

10.3.3.1. AtomicReference

				
AtomicReference<String> atomicRef = new AtomicReference<>("Initial Value");

// 获取值
String value = atomicRef.get(); // "Initial Value"

// 设置值
atomicRef.set("New Value");

// CAS 操作
boolean success = atomicRef.compareAndSet("New Value", "Updated Value");
System.out.println(success); // true(如果当前值仍然是 "New Value")
				
				

10.3.3.2. 线程安全的单例模式

				
class Singleton {
    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();

    public static Singleton getInstance() {
        while (true) {
            Singleton instance = INSTANCE.get();
            if (instance != null) {
                return instance;
            }
            instance = new Singleton();
            if (INSTANCE.compareAndSet(null, instance)) {
                return instance;
            }
        }
    }

    private Singleton() {
        // 私有构造方法
    }
}
				
				

10.3.4. 值设置与获取操作

10.3.4.1. set() / get()

设置新值,使用 volatile 语义,确保新值对其他线程立即可见(遵循 happens-before 原则)。

					
AtomicReference<String> ref = new AtomicReference<>("初始值");
ref.set("新值"); // 原子性地更新为新值					
					
				

AtomicReference 类中的 get() 方法是最常用的读取操作,它提供了强内存顺序保证和即时可见性。

				
import java.util.concurrent.atomic.AtomicReference;

public class StateControlExample {
    private static final AtomicReference<State> state = new AtomicReference<>(State.INIT);

    enum State { INIT, RUNNING, STOPPED }

    public static void main(String[] args) throws InterruptedException {
        // 工作线程
        Thread worker = new Thread(() -> {
            while (state.get() != State.STOPPED) {
                System.out.println("工作中...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("工作线程已停止");
        });

        // 控制线程
        Thread controller = new Thread(() -> {
            try {
                Thread.sleep(3000);
                // 更新状态为 STOPPED
                state.set(State.STOPPED);
                System.out.println("已发送停止命令");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        worker.start();
        controller.start();

        worker.join();
        controller.join();
    }
}
				
				

10.3.4.2. lazySet() / setRelease()

lazySet() 提供了一种在高并发场景下以较低性能开销更新原子引用的方式,适用于对可见性要求不高、但对吞吐量敏感的场景。使用时需明确弱内存顺序的语义,避免在关键业务逻辑中引入不可见性问题。

lazySet() 的实现如下:

				
public final void lazySet(V newValue) {
    VALUE.setRelease(this, newValue);
}				
				
				

以下示例展示了 lazySet() 在标记线程状态时的应用:

				
import java.util.concurrent.atomic.AtomicReference;

public class LazySetExample {
    private static final AtomicReference<State> state = new AtomicReference<>(State.RUNNING);

    enum State { RUNNING, STOPPED }

    public static void main(String[] args) throws InterruptedException {
        // 工作线程
        Thread worker = new Thread(() -> {
            while (state.get() == State.RUNNING) {
                // 执行任务...
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("工作线程已停止");
        });

        worker.start();

        // 主线程:延迟一段时间后停止工作线程
        Thread.sleep(1000);
        
        // 使用 lazySet 设置状态,不要求立即对工作线程可见
        state.lazySet(State.STOPPED);
        System.out.println("已发送停止命令");

        worker.join();
    }
}
				
				

AtomicReference 类中的 setRelease(V newValue) 是 Java 9 引入的原子操作,它提供了释放语义(Release Semantics)的写入操作。与普通的 set() 相比,setRelease() 在内存可见性和重排序限制上有更明确的语义,同时保持了较低的性能开销。

				
import java.util.concurrent.atomic.AtomicReference;

public class PublishSubscribeExample {
    private static final AtomicReference<String> messageRef = new AtomicReference<>(null);

    public static void main(String[] args) throws InterruptedException {
        // 生产者线程:准备并发布消息
        Thread producer = new Thread(() -> {
            // 准备数据(可能涉及多个操作)
            String data = "重要消息";
            int processedData = 42; // 模拟数据处理
            
            // 使用 release 语义发布消息,确保数据准备完成后才发布
            messageRef.setRelease(data);
            System.out.println("消息已发布");
        });

        // 消费者线程:等待并读取消息
        Thread consumer = new Thread(() -> {
            String message;
            // 使用 acquire 语义读取消息,确保看到完整的发布内容
            while ((message = messageRef.getAcquire()) == null) {
                System.out.println("等待消息...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("收到消息: " + message);
        });

        consumer.start();
        producer.start();

        producer.join();
        consumer.join();
    }
}				
				
				

10.3.4.3. setOpaque() / getOpaque()

AtomicReference 类中的 getOpaque() 方法是 Java 9 引入的一个原子操作,它提供了一种以弱内存顺序读取引用值的方式。与 get() 方法相比,getOpaque() 的可见性保证较弱,但性能开销可能更低。

				
import java.util.concurrent.atomic.AtomicReference;

public class OpaqueReadExample {
    private static final AtomicReference<String> ref = new AtomicReference<>("初始值");

    public static void main(String[] args) {
        // 线程1:更新值
        Thread writer = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ref.set("新值");
            System.out.println("写入完成: " + ref.get());
        });

        // 线程2:使用 getOpaque() 读取值
        Thread reader = new Thread(() -> {
            String value;
            do {
                // 使用弱内存顺序读取
                value = ref.getOpaque();
                System.out.println("读取到: " + value);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while (!"新值".equals(value));
            System.out.println("最终读取到: " + value);
        });

        writer.start();
        reader.start();

        try {
            writer.join();
            reader.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}				
				
				

10.3.4.4. setAcquire() / getAcquire()

AtomicReference 类中的 getAcquire() 方法是,提供了一种以获取语义(Acquire Semantics)读取引用值的方式。与 getOpaque() 相比,getAcquire() 提供了更强的内存可见性保证,但比 get() 更轻量。

				
import java.util.concurrent.atomic.AtomicReference;

public class PublishSubscribeExample {
    private static final AtomicReference<String> messageRef = new AtomicReference<>(null);

    public static void main(String[] args) {
        // 生产者线程:发布消息
        Thread producer = new Thread(() -> {
            // 准备数据
            String data = "重要消息";
            // 其他操作...
            // 使用 release 语义发布消息
            messageRef.setRelease(data);
            System.out.println("消息已发布");
        });

        // 消费者线程:订阅消息
        Thread consumer = new Thread(() -> {
            String message;
            // 使用 acquire 语义读取消息
            while ((message = messageRef.getAcquire()) == null) {
                System.out.println("等待消息...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("收到消息: " + message);
        });

        consumer.start();
        producer.start();

        try {
            producer.join();
            consumer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}				
				
				

10.3.4.5. setPlain() / getPlain()

AtomicReference 类中的 getPlain() 方法提供了最弱的内存顺序保证,用于读取引用值。与其他读取方法相比,getPlain() 的性能开销最低,但也牺牲了更多的内存可见性保证。

				
import java.util.concurrent.atomic.AtomicReference;

public class CounterExample {
    private static final AtomicReference<Integer> counter = new AtomicReference<>(0);

    public static void main(String[] args) throws InterruptedException {
        // 写线程:递增计数器
        Thread writer = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.updateAndGet(v -> v + 1);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 读线程:使用 getPlain() 读取计数器
        Thread reader = new Thread(() -> {
            int value;
            while ((value = counter.getPlain()) < 1000) {
                System.out.println("当前计数(getPlain): " + value);
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("最终计数: " + value);
        });

        writer.start();
        reader.start();

        writer.join();
        reader.join();
    }
}				
				
				

10.3.4.6. getAndSet()

将引用值原子性地设置为新值,并返回旧值。

					
AtomicReference<String> ref = new AtomicReference<>("旧值");
String oldValue = ref.getAndSet("新值");
System.out.println("旧值: " + oldValue); // 输出 "旧值"
System.out.println("新值: " + ref.get()); // 输出 "新值"					
					
				

10.3.4.7. compareAndSet()

CAS 操作:比较当前值是否等于预期值 expect,如果相等则原子性地将其更新为 update。

					
AtomicReference<String> ref = new AtomicReference<>("初始值");

// 只有当当前值为"初始值"时,才更新为"新值"
boolean success = ref.compareAndSet("初始值", "新值");
System.out.println("更新结果: " + success); // 输出 true