Java内存模型

简介:

一 JAVA内存模型JMM  

   Java的内存模型,也就是JVM所设置的内存模型。Java内存模型分为主存储器(主内存)和工作存储器(工作内存),这里的存储器与计算机硬件所讲的不一样。

   主存储器,就是实例位置所在的区域,所有的实例都存在主存储器内,并且实例的字段也位于这里。主存储器为所有的线程所共享,主内存主要对应于Java堆中对象的实例数据部分。

   工作存储器,它是各个线程所拥有的独立专门的作业区。在工作存储器中,存在有主存储器中必要的拷贝,称为工作拷贝或者变量副本。工作内存对应于虚拟机中的部分区域。

  每个线程都位于各自的工作存储器中,每个线程都不能直接的对存储器中字段进行引用或者赋值操作。

   当线程欲引用字段的值时候,会一次将值从主存储器拷贝到工作存储器中,然后再引用该工作拷贝的字段。当同一个线程再次引用同一个字段的值时候,可能会引用刚才的工作拷贝,也可能会重新从主存储器拷贝到工作存储器。

   当线程欲将值指定给字段的时候,会一次将值指定给位于工作存储器上的工作拷贝。指定完后,工作拷贝的内容则会映射到主存储器中。至于什么时候映射,是都JVM决定的。当同一个线程多次对于同一个字段指定的时候,线程可能只会对工作拷贝进行指定,也有可能会每次指定后,马上拷贝到主存储器中。


二 内存间的交互操作

  主内存与工作内存之间的交互操作定义了8种原子性操作。具体如下:

 1 lock(锁定):作用于主内存的变量,将一个变量标识为一条线程独占状态

  2 unlock(解锁):作用于主内存的变量,将一个处于锁定状态的变量释放出来

  3 read(读取):作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存中

  4 load(载入):作用于工作内存的变量,把read传输的变量值放入或者拷贝到工作内存的变量副本

  5 use(使用):作用于工作内存的变量,表示线程引用工作内存中的变量值,将工作内存中的一个变量的值传递给执行引擎

  6 assign(赋值):作用于工作内存的变量,表示线程将指定的值赋值给工作内存中的某个变量。

  7 store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送给主内存中

  8 write(写入):作用于主内存的变量,将store传递的变量值放入到主内存中对应的变量里


三 Java同步机制

  Java中同步包括:线程同步和内存同步。

synchronized:线程同步和内存同步

  线程的同步指的就是利用synchronized设置一个临界区,使得只有同时一个线程在该临界区执行。由synchronized所指定的临界区,来控制线程的操作。

  欲进入synchronized时候,线程的工作存储器如果有未映射到主存储器的工作拷贝,该内容就会被强制写入主存储器,并且会将工作存储器的工作拷贝全部丢弃清除掉。

  欲退出synchronized时候,线程会将工作存储器中未映射到主存储器的工作拷贝强制写入主存储器中。但是并不会清除或丢弃自己的工作存储器。

   在synchronized中,不管是方法还是代码块,内存同步仅仅会在线程“欲进入”与“欲退出”synchronized时候进行内存同步。如果是“在synchronized内部”或“正在synchronized外部”,不一定会有内存的同步。

Volatile:内存同步

   对于关键字Volatile,它仅仅是进行内存的同步,并不会涉及线程的同步,利用Volatile修饰的字段可以允许多个线程同时访问。当线程欲引用volatile字段的值,就会从主存储器中拷贝到工作存储器里。对于指定给volatile字段值后,工作存储器的内容都会立刻马上映射到主存储器中。

    对于Volatile修饰的变量,在读取的过程与非Volatile变量差不多,只是会在写入操作上慢一点

一个变量为Volatile类型具备的两种特性:

   1 保证此变量对所有线程的可见性。指的就是当一个线程修改了这个变量的值后,新值对于其他线程来讲师马上可以看到的。

   2 禁止指令重排序优化。由于指令会在执行中进行重排序进行优化,利用Volatile后,可以保证该指定不会被重新排序,也就是在程序中位于Volatile之前的先执行,位于Volatile之后的在它之后执行,不会混合到前面或者后面指令的重排序中。

一般仅仅在以下场合中选择使用volatiole:

   1 对变量的写入操作不依赖于变量的当前值

   2 变量不会与其它状态变量一起纳入不变性条件中

   3 在访问变量的时候不需要进行加锁

   由于long和double是64位的,JVM在处理这种类型的读写操作可以划分为两次的32位操作,所以必须要注意这两种类型的共享操作


四 原子性、可见性和有序性

  Java内存模型对于并发处理都是基于原子性、可见性和有序性进行设计的。

原子性  Java内部6种基本类型都是采用原子性操作的,当需要扩大原子性操作,就可以利用Java内存模型中的lock和unlock来实现,这两种方法对应高层次的字节码指令是monitorenter和monitorexit,而这两个字节码指令反映到Java代码中就是同步synchronized

可见性  指的就是当一个线程修改了共享变量后,其他线程都马上看到这个变量值。在Java是利用volatile、synchronized以及final,前两种已经在前面说明了。对于final指的就是,凡是被final修饰的字段在构造器一旦被初始化完成后,那么其他线程就能看到这个final字段的值

有序性  Java运用的就是volatile和synchronized实现的,volatile保证了禁止指令重排序,保证了该指令在程序中原来的顺序。而synchronized保证了一个时刻仅允许一个线程对其进行lock操作。


五 happens-before先行发生原则

  “先行发生”,可以主要用来判断在并发中数据是否存在竞争,线程是否安全。

  “先行发生”,是Java内存模型中定义的两个操作之间的偏序关系。即操作A先行发生于操作B,那么就是在发生操作B之前,操作A产生的影响能够被操作B观察到。Java内存模型有几个先行发生的关系。在并发测试中,如果两个操作不再以下类别中,那么其实际执行就没有顺序保障,即线程是不安全的。

程序顺序规则:如果程序中操作A在操作B之前,那么在线程的内部中A操作将在B操作之前。

监视器规则:在监视器锁上的解锁操作先行发生于后面对于同一个锁的枷锁操作。

volatile变量规则:对于一个volatile变量的写操作先行发生于对该变量的读操作之前。

线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。

线程结束规则:线程中所有的操作都先行发生于对此线程的终止测试。

线程中断规则:当一个线程在另一个线程上被调用interrupt时,必须在被中断线程检测到interrupt调用之前执行。

终结器规则:对象的构造器函数必须在启动该对象的终结器finalize()方法之前完成。

传递性:如果操作A在操作B之前执行,操作B在操作C之前执行,那么操作A必须在操作C之前执行


注意:凡是多个线程所共享的对象,会对对象的状态进行修改等操作的时候,一般由synchronized或volatile来保护。




本文转自 zhao_xiao_long 51CTO博客,原文链接:http://blog.51cto.com/computerdragon/1208398

相关文章
|
24天前
|
Java 编译器 开发者
深入理解Java内存模型(JMM)及其对并发编程的影响
【9月更文挑战第37天】在Java的世界里,内存模型是隐藏在代码背后的守护者,它默默地协调着多线程环境下的数据一致性和可见性问题。本文将揭开Java内存模型的神秘面纱,带领读者探索其对并发编程实践的深远影响。通过深入浅出的方式,我们将了解内存模型的基本概念、工作原理以及如何在实际开发中正确应用这些知识,确保程序的正确性和高效性。
|
4天前
|
缓存 easyexcel Java
Java EasyExcel 导出报内存溢出如何解决
大家好,我是V哥。使用EasyExcel进行大数据量导出时容易导致内存溢出,特别是在导出百万级别的数据时。以下是V哥整理的解决该问题的一些常见方法,包括分批写入、设置合适的JVM内存、减少数据对象的复杂性、关闭自动列宽设置、使用Stream导出以及选择合适的数据导出工具。此外,还介绍了使用Apache POI的SXSSFWorkbook实现百万级别数据量的导出案例,帮助大家更好地应对大数据导出的挑战。欢迎一起讨论!
|
16天前
|
缓存 算法 Java
Java中的内存管理:理解与优化
【10月更文挑战第6天】 在Java编程中,内存管理是一个至关重要的主题。本文将深入探讨Java内存模型及其垃圾回收机制,并分享一些优化内存使用的策略和最佳实践。通过掌握这些知识,您可以提高Java应用的性能和稳定性。
42 4
|
16天前
|
存储 监控 算法
Java中的内存管理:理解Garbage Collection机制
本文将深入探讨Java编程语言中的内存管理,着重介绍垃圾回收(Garbage Collection, GC)机制。通过阐述GC的工作原理、常见算法及其在Java中的应用,帮助读者提高程序的性能和稳定性。我们将从基本原理出发,逐步深入到调优实践,为开发者提供一套系统的理解和优化Java应用中内存管理的方法。
|
21天前
|
Java
java操作内存,简单讲解varhandle的使用
本文介绍了Java中VarHandle的使用,它是一种从JDK 9开始引入的用于高效访问对象字段的特性。文章通过示例代码展示了如何通过VarHandle操作对象的字段,包括设置和获取字段值,以及如何通过MethodHandles.lookup().findVarHandle()方法获取VarHandle实例。VarHandle提供了一种比反射更高效的内存操作方式,并且支持原子操作。
30 0
java操作内存,简单讲解varhandle的使用
|
2天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
9天前
|
存储 监控 算法
Java中的内存管理与垃圾回收机制解析
本文深入探讨了Java编程语言中的内存管理方式,特别是垃圾回收机制。我们将了解Java的自动内存管理是如何工作的,它如何帮助开发者避免常见的内存泄漏问题。通过分析不同垃圾回收算法(如标记-清除、复制和标记-整理)以及JVM如何选择合适的垃圾回收策略,本文旨在帮助Java开发者更好地理解和优化应用程序的性能。
|
11天前
|
存储 Java
Java内存模型
【10月更文挑战第11天】Java 内存模型(JMM)是 Java 虚拟机规范中定义的多线程内存访问机制,解决内存可见性、原子性和有序性问题。它定义了主内存和工作内存的概念,以及可见性、原子性和有序性的规则,确保多线程环境下的数据一致性和操作正确性。使用 `synchronized` 和 `volatile` 等同步机制可有效避免数据竞争和不一致问题。
22 3
|
11天前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
21 2
|
14天前
|
Java 数据挖掘 数据库连接
Java使用直接内存的好处
综上所述,Java直接内存的使用为开发者提供了一种绕过JVM堆限制、直接高效操作内存资源的途径,特别适用于高吞吐量、低延迟和大规模数据处理的场景。虽然直接内存的使用需要更精细的管理以避免内存泄漏和过度消耗系统资源,但恰当的利用能够显著提升应用的性能表现,是现代高性能Java应用不可或缺的工具之一。
14 2