java学习笔记
java内存模型的三大特性
本 文 目 录
在Java的并发编程世界中,内存模型是一个不可或缺的概念。它定义了线程之间如何共享数据,以及在多线程环境下如何保证数据的可见性、原子性和有序性。了解Java内存模型(JMM)对于编写高效且线程安全的代码至关重要。本文将深入探讨JMM的三大特性:原子性、可见性和有序性,并通过代码案例加以阐释。
原子性
定义与目的
原子性是指一个操作要么完全执行,要么完全不执行,不会在执行过程中被其他线程中断的特性【1】。原子性保证了在并发环境下,对共享数据的操作不会因为线程交错执行而导致数据不一致的问题。
核心类与方法
在Java中,java.util.concurrent.atomic
包下的原子类提供了原子性操作的支持。例如,AtomicInteger
提供了对整数进行原子操作的方法,如incrementAndGet()
和compareAndSet()
。
使用场景
当需要对共享资源进行计数或者累加时,原子性操作就显得尤为重要。例如,统计网站访问量或者实现一个线程安全的计数器。
代码案例
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static final AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> counter.incrementAndGet()).start();
}
// 等待所有线程执行完毕
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println("Final counter value: " + counter.get());
}
}
可见性
定义与目的
可见性是指当一个线程修改了共享变量的值,其他线程能够立即看到这个改变的特性【1】。在没有可见性保证的情况下,线程可能会读取到过时的值,导致程序逻辑错误。
核心类与方法
volatile
关键字是Java中实现可见性的一种机制。当一个变量被声明为volatile
,它会告诉JVM,每次访问这个变量时都要从主内存中读取,而不是从线程的工作内存中读取。
使用场景
volatile
适用于状态标记、布尔标志等需要立即反映修改的场景。例如,控制线程的运行状态,如Thread
对象的isInterrupted()
方法。
代码案例
public class VisibilityExample {
private volatile boolean running = true;
public void stop() {
running = false;
}
public void doWork() {
while (running) {
// 执行任务
}
}
}
有序性
定义与目的
有序性是指程序执行的顺序与代码编写的顺序一致【1】。在多线程环境中,由于编译器和处理器可能会进行指令重排序,这可能会导致程序执行顺序与预期不符。JMM通过内存屏障和happens-before
原则来保证操作的有序性。
核心类与方法
synchronized
关键字和各种锁(如ReentrantLock
)可以保证代码块或者方法的执行顺序。此外,java.lang.Long
类中的compareAndSwap
方法系列也用于保证操作的有序性。
使用场景
在需要确保多个线程按照特定顺序执行操作时,有序性就显得尤为重要。例如,更新多个共享变量的顺序依赖关系。
代码案例
public class OrderlinessExample {
private int value = 0;
private boolean flag = true;
public synchronized void increment() {
if (flag) {
value++;
flag = false;
}
}
public synchronized void decrement() {
if (!flag) {
value--;
flag = true;
}
}
public static void main(String[] args) {
OrderlinessExample example = new OrderlinessExample();
Thread t1 = new Thread(example::increment);
Thread t2 = new Thread(example::decrement);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final value: " + example.value);
}
}
特性对比表格
特性 | 定义 | 核心机制 | 使用场景 |
---|---|---|---|
原子性 | 操作不可中断 | AtomicInteger , AtomicLong 等 |
计数器, 累加器 |
可见性 | 修改立即可见 | volatile 关键字 |
状态标记, 布尔标志 |
有序性 | 执行顺序一致 | synchronized , 内存屏障 |
多线程操作顺序依赖 |
通过上述分析,我们可以看到JMM的三大特性在多线程编程中扮演着至关重要的角色。它们确保了在并发环境下,数据的完整性和程序的正确性。理解并合理运用这些特性,将有助于我们编写出更加健壮和高效的Java程序。