当前位置:网站首页 > 技术博客 > 正文

java内存模型原理



Java内存模型详解:JMM, Happens-Before原则及其应用

Java内存模型(JMM,Java Memory Model)是Java虚拟机规范的一部分,它定义了Java程序中各种变量(线程共享的实例字段、静态字段和数组)的访问规则。这种规定为Java提供了一种在并发编程环境中的内存可见性保证,避免了因为多线程之间的数据不同步而导致的各种难以排查的问题。

1. JMM的基本概念

在深入理解Java内存模型之前,首先需要了解以下几个核心概念:

  • 主内存与工作内存:在JMM中,所有实例字段、静态字段和数组都存储在主内存中,它是一个共享的资源。每个线程都有自己的工作内存,工作内存存储了被该线程使用到的变量的主内存副本拷贝。
  • 内存操作:JMM定义了8种原子操作来直接与主内存交互,但这里为了简单化,我们只关心以下几种:read(读取主内存变量到工作内存)、load(工作内存变量就绪)、use(线程使用工作内存变量)、assign(线程给工作内存变量赋值)、store(将工作内存变量的值写入主内存)、write(将store的值覆盖主内存的值)。
2. Happens-Before原则

为了保证内存的可见性、有序性以及原子性,JMM引入了原则。这是一种保证两个操作之间的先后顺序的原则。如果一个操作另一个操作,那么第一个操作的结果对第二个操作是可见的。

以下是几个基本的规则:

  • 程序顺序规则:一个线程内,按照代码顺序,书写在前面的操作happens-before于书写在后面的操作。
  • 锁的规则:解锁操作happens-before于后续对同一个锁的加锁操作。
  • volatile变量规则:对一个volatile字段的写操作happens-before于后续对该volatile字段的读操作。
  • 线程启动规则:Thread对象的start()方法调用happens-before于此线程的每一个操作。
  • 线程终止规则:线程中的所有操作都happens-before于其他线程检测到此线程已经终止。

以下是一个简单的代码例子,用于展示的规则:

 
  

在上面的代码中,方法中的写操作于方法中的读操作。这意味着,如果一个线程调用了方法并更新了的值,随后另一个线程调用方法,那么第二个线程会看到的新值。

注意: volatile关键字并不能保证复合操作的原子性,它只能保证单一读/写操作的可见性。

至此,我们已经对JMM和原则有了一个初步的了解。

3. 内存可见性和指令重排序

为了提高程序的执行性能,处理器、编译器或者JVM在不改变单线程程序语义的前提下,可能会对输入代码进行优化,这导致了两个问题:内存可见性指令重排序

内存可见性

当多个线程访问共享变量时,一个线程修改了这个变量的值,其他线程可能立即看不到这个修改。这就是内存可见性问题。

例如:

 
  

理论上,应该输出,但由于内存可见性问题,它可能输出,或者这行代码根本不会执行。

使用关键字可以保证变量的可见性,因为它遵循规则。

指令重排序

指令重排序是指CPU为了提高程序运行效率,对代码进行排序优化,但可能会导致程序并行执行时出现数据不一致的情况。

考虑以下代码:

 
  

假设和分别在两个不同的线程中执行,我们期望输出的值为。但由于指令重排序,语句1和语句2的执行顺序可能会互换,这样在中,的值可能还是。

使用关键字或关键字均可以避免指令重排序的问题。

4. 和

这两个关键字都可以用于确保多线程间的内存可见性。

  • volatile: 仅保证变量的读写操作不会被重排序,并且保证变量的写操作对其他线程是可见的。但它不能保证复合操作的原子性。
  • synchronized: 它既可以确保块内的操作是原子性的,又可以确保块内的内存操作对其他线程是可见的。

例子:

 
  

在这个例子中,方法和方法都被关键字修饰,确保了的读写操作不仅是原子性的,而且对其他线程是可见的。

5. JMM的总结

JMM为Java提供了一个在并发编程环境中的内存可见性、有序性以及原子性的保证。通过理解其核心概念和使用相应的关键字,我们可以更好地编写并发程序。

6. Java并发工具

Java为开发者提供了一个非常强大的并发工具库,即包。这个包中包含了许多类和接口,它们可以帮助开发者更容易地写出高效、健壮和可扩展的并发应用。

java.util.concurrent.atomic

这个子包包含了一些原子类,它们利用了CPU的底层指令,实现了高效的原子操作。以下是一些常用的原子类:

例如,提供了一个方法,该方法原子地增加整数的值并返回增加后的值。

 
  

这些原子操作都是线程安全的,并且它们的性能通常比关键字更好。

java.util.concurrent.locks

有时候,您可能需要比关键字更细粒度的锁定。在这种情况下,您可以使用包中的。

 
  

这个锁还提供了其他高级特性,例如条件变量、锁公平性和锁超时。

java.util.concurrent

这个包还包含了其他的并发工具,例如:

  • 和 :用于并发任务的执行。
  • 和 :代表异步计算的结果。
  • 、 和 :用于线程之间的同步。

以下是一个使用和的简单示例:

 
  

这个示例中,我们创建了一个固定大小的线程池,并提交了一个简单的任务。会等待任务完成并返回结果。

总结

通过结合Java内存模型(JMM)的知识和包中的工具,Java开发者可以编写出高效、健壮和可扩展的并发应用。确保您熟悉了上面提到的各种工具和它们的用途,这样您在编写并发代码时可以做出明智的决策。

  • 上一篇: 开窗函数partition by
  • 下一篇: html菜鸟工具
  • 版权声明


    相关文章:

  • 开窗函数partition by2025-07-05 22:30:03
  • linux中time函数2025-07-05 22:30:03
  • 移位运算的基本原理2025-07-05 22:30:03
  • xlwt 合并单元格2025-07-05 22:30:03
  • jsoncpp库的使用2025-07-05 22:30:03
  • html菜鸟工具2025-07-05 22:30:03
  • javatreemap优点2025-07-05 22:30:03
  • 运算符重载时有哪些限制2025-07-05 22:30:03
  • qfile remove2025-07-05 22:30:03
  • securecrt8.5破解2025-07-05 22:30:03