JMM介绍
1.什么是JMM?
Java内存模型(Java Memory Model简称JMM)是一种抽象的概念,并不真实存在,Java内存模型定义了Java程序中多线程之间如何交互以及如何与主内存之间进行数据同步的规范。来确保多线程程序的正确性。Jvm运行的程序的实体是线程,而每个线程运行时,都会创建一个工作内存(也叫栈空间),来保存线程所有的私有变量。而JMM内存模型规范中规定所有的变量都存储在主内存中,而主内存中的变量是所有的线程都可以共享的,而对主内存中的变量进行操作时,必须在线程的工作内存进行操作,首先将主内存的变量copy到工作内存,进行操作后,再将变量刷回到主内存中。所有线程只有通过主内存来进行通信。
Java内存模型定义了以下重要概念:
- 主内存(Main Memory):所有线程共享的内存区域,用于存储变量和实例对象。
- 工作内存(Working Memory):每个线程私有的内存区域,用于存储主内存中的变量副本。
- 内存屏障(Memory Barrier):用于同步主内存与工作内存中的数据,在多线程环境下保证数据的一致性和可见性。
- Happens-Before关系:一种偏序关系,用于指定程序中操作之间的执行顺序,基于此关系来保证数据的可见性和一致性。
2.JMM的三大特性:
JMM的三大特性:原子性、可见性、有序性。
原子性(Atomicity):
- 原子性指的是一个操作是不可分割的最小单位,要么全部执行完毕,要么完全不执行,不存在执行了一半的情况。
- 在Java中,原子性通常通过synchronized关键字、Lock锁或者原子类(Atomic classes)来实现。这些机制可以确保某些操作以原子方式执行,从而避免了多线程环境下的数据竞争和不一致问题。
可见性(Visibility):
- 可见性指的是当一个线程修改了共享变量的值后,其他线程能够立即看到这个修改的结果。换句话说,当一个线程对共享变量进行了修改,其他线程应该能够立即感知到这个变化,而不是一直读取旧值。
- 在Java中,可见性问题可以通过synchronized关键字、volatile关键字、显式锁(如Lock)以及特定的并发工具来解决。
有序性(Ordering):
- 有序性指的是程序执行的顺序与代码中的顺序相匹配。在单线程环境下,代码的执行顺序通常是按照编写的顺序执行的,但在多线程环境下,由于指令重排序等原因,代码的执行顺序可能会与编写的顺序不一致。
- Java内存模型通过Happens-Before关系来保证程序中操作的执行顺序,这种偏序关系规定了在不同线程中的操作之间的执行顺序,确保了程序的有序性。
3.JMM如何解决原子性&可见性&有序性
原子性(Atomicity):
- JMM通过使用synchronized关键字、volatile关键字和原子类(Atomic classes)等机制来保证原子性。
- 使用synchronized关键字或者显式锁(如Lock)可以确保代码块在同一时刻只能被一个线程执行,从而避免了多线程环境下的竞态条件。
- 使用volatile关键字可以保证被修饰的变量的写操作对其他线程可见,并且禁止指令重排序,从而确保了一些简单操作的原子性和可见性。
- 原子类(Atomic classes),如AtomicInteger、AtomicLong等,提供了一些原子性操作,比如原子地增加、减少等操作,从而避免了使用锁的开销。
可见性(Visibility):
- JMM通过volatile关键字、synchronized关键字、Lock锁和Happens-Before关系来保证可见性。
- 使用volatile关键字可以确保被修饰的变量的写操作对其他线程立即可见,即使在不同的线程间发生了缓存一致性的问题。
- 使用synchronized关键字或者显式锁(如Lock)可以保证代码块在同一时刻只能被一个线程执行,当线程释放锁时,会将该线程对共享变量的修改刷新到主内存,从而保证了可见性。
有序性(Ordering):
- volatile关键字保证了有序性,synchronized和Lock也保证了有序性(因为同一时刻只允许一个线程访问同步代码块,自然保证了线程之间在同步代码块的有序执行)。