导语:在高并发编程的世界里,Java开发者最常面对的挑战就是线程安全问题。为什么多线程操作共享变量结果总是不对?为什么线程看不到其他线程对变量的修改?为什么单例模式在多线程环境下会失效?这些问题的背后,都指向了Java内存模型(JMM)和并发编程的三大特性。本文将带你深入底层,逐一破解这些谜团。
一、Java内存模型(JMM):并发编程的基石
在探讨并发特性之前,我们必须先了解Java内存模型(JMM)。JMM的存在是为了屏蔽不同硬件和操作系统在内存访问上的差异,确保Java程序能够“一次编写,到处运行”。
JMM的核心机制:
- 主内存与工作内存:所有变量都存储在主内存中。线程在操作变量时,会从主内存拷贝一份副本到自己的工作内存(CPU缓存)中进行计算,计算完成后再写回主内存。
- 并发三要素:为了解决并发环境下的数据一致性问题,JMM定义了三个关键概念:原子性、可见性和有序性。
二、原子性(Atomicity):不可分割的操作单元
1. 什么是原子性?
原子性是指一个操作是不可中断的,即使在多线程环境下,一个线程在执行某个操作时,其他线程也无法干扰。要么操作完全执行成功,要么完全不执行。
2. 经典的线程安全问题演示
我们常遇到的i++操作在多线程下结果不准确,就是典型的原子性问题。i++实际上包含了三个步骤:读取变量值、加1、写回主内存。如果这三步不能作为一个整体执行,就会导致数据覆盖。
private static int count = 0; public static void increment() { count++; // 非原子操作 } // 多线程调用后,结果往往小于预期