在 Java 并发编程中,原子性(Atomicity)、可见性(Visibility) 和 有序性(Ordering) 是三个核心概念,它们共同保证了多线程环境下程序的正确性。以下是它们的详细解释:
1. 原子性(Atomicity)
原子性是指一个操作是不可分割的,要么全部执行成功,要么全部不执行,不会出现部分执行的情况。
关键点:
- 单个操作:对于单个变量的读写操作,Java 保证是原子的(如
int
、boolean
等基本类型的读写)。 - 复合操作:对于多个操作组合(如
i++
),Java 不保证原子性,需要额外同步机制(如synchronized
或Atomic
类)。
如何保证原子性:
- 使用
synchronized
关键字。 - 使用
java.util.concurrent.atomic
包中的原子类(如AtomicInteger
)。 - 使用锁(如
ReentrantLock
)。
2. 可见性(Visibility)
可见性是指一个线程对共享变量的修改,能够及时被其他线程看到。
关键点:
- 线程缓存:每个线程有自己的工作内存,可能会缓存共享变量的副本,导致其他线程看不到最新的值。
- 内存屏障:通过内存屏障(如
volatile
、synchronized
)强制刷新线程的工作内存,保证可见性。
如何保证可见性:
- 使用
volatile
关键字。 - 使用
synchronized
关键字。 - 使用
java.util.concurrent
包中的工具类(如Atomic
类)。
3. 有序性(Ordering)
有序性是指程序执行的顺序按照代码的先后顺序执行。但在多线程环境下,由于指令重排序(编译器或处理器优化),代码的执行顺序可能会被打乱。
关键点:
- 指令重排序:为了提高性能,编译器和处理器可能会对指令进行重排序。
- Happens-Before 规则:Java 内存模型(JMM)定义了一系列规则,保证某些操作的有序性。
如何保证有序性:
- 使用
volatile
关键字(禁止指令重排序)。 - 使用
synchronized
关键字。 - 使用
final
关键字(保证构造函数的初始化顺序)。
三者的关系
- 原子性:关注操作的不可分割性。
- 可见性:关注线程间数据的及时更新。
- 有序性:关注代码的执行顺序。
它们共同作用,确保多线程程序的正确性。
示例:综合应用
以下是一个综合示例,展示了如何通过 volatile
和 synchronized
保证原子性、可见性和有序性:
public class ConcurrencyExample {
private volatile boolean flag = false; // 保证可见性和有序性
private int counter = 0; // 需要同步保证原子性
public void writer() {
counter = 42; // 操作 1
flag = true; // 操作 2
}
public void reader() {
if (flag) { // 操作 3
System.out.println("Counter: " + counter); // 操作 4
}
}
public synchronized void increment() {
counter++; // 保证原子性
}
public static void main(String[] args) {
ConcurrencyExample example = new ConcurrencyExample();
Thread writerThread = new Thread(() -> {
example.writer();
});
Thread readerThread = new Thread(() -> {
example.reader();
});
writerThread.start();
readerThread.start();
// 等待线程执行完成
try {
writerThread.join();
readerThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
总结
- 原子性:通过
synchronized
或Atomic
类保证。 - 可见性:通过
volatile
或synchronized
保证。 - 有序性:通过
volatile
或synchronized
保证。
THE END
暂无评论内容