Java内存模型(JMM)是Java并发编程中的核心概念,它定义了线程如何和何时可以看到其他线程修改过的共享变量的值,以及在必须时如何同步的访问共享变量。本文将深入探讨JMM的核心概念和最佳实践。
1. Java内存模型基础
1.1 内存模型的基本概念
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class MemoryModelDemo {
private int x = 0; // 共享变量
private volatile boolean flag = false; // volatile变量
public void writer() {
x = 42; // 写入共享变量
flag = true; // 写入volatile变量,确保可见性
}
public void reader() {
if (flag) { // 读取volatile变量
System.out.println(x); // 保证能看到writer()中的写入
}
}
}
|
1.2 happens-before关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class HappensBefore {
private int counter = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) { // 同步块建立happens-before关系
counter++;
}
}
public int getCounter() {
synchronized (lock) {
return counter;
}
}
}
|
2. volatile关键字
2.1 可见性保证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class VolatileVisibility {
private volatile boolean flag = false;
private int data = 0;
public void producer() {
data = 100; // 1
flag = true; // 2 volatile写入
}
public void consumer() {
while (!flag) { // volatile读取
Thread.yield();
}
assert data == 100; // 一定能看到producer中的写入
}
}
|
2.2 禁止重排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class VolatileReordering {
private volatile int state = 0;
private int[] data = new int[10];
public void prepare() {
for (int i = 0; i < data.length; i++) {
data[i] = i; // 1
}
state = 1; // 2 volatile写入,确保1不会重排序到2之后
}
public void use() {
if (state == 1) { // volatile读取
for (int item : data) {
// 使用数据,确保能看到prepare()中的初始化
System.out.println(item);
}
}
}
}
|
3. synchronized关键字
3.1 监视器锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class SynchronizedExample {
private final Object mutex = new Object();
private int count = 0;
public void increment() {
synchronized (mutex) {
count++; // 临界区
}
}
public synchronized int getCount() { // 方法级同步
return count;
}
}
|
3.2 锁的重入性
1
2
3
4
5
6
7
8
9
|
public class ReentrantLockExample {
public synchronized void outer() {
inner(); // 可以再次获取同一个锁
}
public synchronized void inner() {
// 方法体
}
}
|
4. final关键字
4.1 final域的重排序规则
1
2
3
4
5
6
7
8
9
10
11
12
|
public class FinalExample {
private final int[] arrays; // final数组引用
private int[] data;
public FinalExample() {
arrays = new int[10]; // final写
for (int i = 0; i < arrays.length; i++) {
arrays[i] = i; // 对final域的初始化
}
data = new int[10]; // 普通写
}
}
|
5. 原子性操作
5.1 原子变量类
1
2
3
4
5
6
7
8
9
10
11
12
|
public class AtomicExample {
private AtomicInteger counter = new AtomicInteger(0);
private AtomicReference<String> ref = new AtomicReference<>("initial");
public void increment() {
counter.incrementAndGet(); // 原子递增
}
public boolean compareAndSet(String expect, String update) {
return ref.compareAndSet(expect, update); // CAS操作
}
}
|
6. 内存屏障
1
2
3
4
5
6
7
8
9
10
11
12
|
public class MemoryBarrierExample {
private volatile int storeLoad = 0;
public void example() {
int local = 1;
// Store-Store屏障
storeLoad = 1; // volatile写入
// Store-Load屏障
int other = storeLoad; // volatile读取
// Load-Load和Load-Store屏障
}
}
|
7. 线程安全性实践
7.1 不可变对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public final class ImmutableValue {
private final int value;
private final List<Integer> list;
public ImmutableValue(int value, List<Integer> list) {
this.value = value;
this.list = new ArrayList<>(list); // 防御性复制
}
public int getValue() {
return value;
}
public List<Integer> getList() {
return new ArrayList<>(list); // 返回副本
}
}
|
7.2 安全发布
1
2
3
4
5
6
7
8
9
|
public class SafePublication {
private static class Holder {
static final SafePublication INSTANCE = new SafePublication();
}
public static SafePublication getInstance() {
return Holder.INSTANCE; // 线程安全的延迟初始化
}
}
|
最佳实践建议
- 优先使用不可变对象
- 正确使用volatile关键字
- 遵循happens-before规则
- 避免过度同步
- 使用java.util.concurrent包中的工具类
性能优化技巧
- 减少锁竞争
1
2
3
4
5
6
7
8
9
10
|
public class LockGranularity {
private final Map<String, List<String>> map =
new ConcurrentHashMap<>(); // 使用并发容器
public void addItem(String key, String item) {
List<String> list = map.computeIfAbsent(key,
k -> new CopyOnWriteArrayList<>());
list.add(item); // 细粒度的锁
}
}
|
- 使用ThreadLocal避免共享
1
2
3
4
5
6
7
8
|
public class ThreadLocalExample {
private static final ThreadLocal<DateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public String formatDate(Date date) {
return dateFormat.get().format(date);
}
}
|
总结
深入理解Java内存模型对于编写正确的并发程序至关重要。通过合理使用synchronized、volatile等同步机制,以及遵循happens-before规则,我们可以确保程序的线程安全性。同时,通过采用适当的最佳实践和性能优化技巧,可以在保证正确性的同时提高程序的性能。