文档章节

java 内存模型

呆萌的我
 呆萌的我
发布于 2015/10/13 20:12
字数 1448
阅读 4
收藏 0

java模型规定:所有变量都要存储在主内存(Main Memory)中,每一个线程都有自己的工作内存(Working Memory),线程中的工作内存中保存了被该线程使用的变量的主内存副本拷贝,线程对变量的操作(读取,复制)都必须在工作内存进行,而不能直接读写内存中的变量。而不同线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要通过主内存完成。

学过操作系统的都应该明白,高速缓存为了缓解CPU与硬件的速度差等问题,主内存对应的是物理硬件,为了提高运行速度,虚拟机会让工作内存有限存储与寄存器和高速缓存中,因为程序运行时主要读写的工作内存。

主内存和工作内存之间有8种操作完成,虚拟机实现每个操作都是原子的,不可再分的:

  • luck: 作用在主内存的变量,它把一个变量标示为一条线程独占的状态。
  • unluck: 作用在主内存的变量,它把一个处于锁定状态的变量释放出来,给其他线程访问。
  • read: 作用在主内存的变量,它把一个变量从主内存传输到线程的工作内存中,以便随后的load动作使用。
  • load: 作用在工作内存的变量,它把read操作从主内存中得到的变量放在工作内存变量的副本中。
  • use: 作用在工作内存的变量,它把工作中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用的变量的值的字节码指令时将会执行这个操作。
  • assign: 作用在工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令式执行这个操作。
  • store: 作用在工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
  • write: 作用在主内存的变量,它吧store操作从工作内存中得到的变量值放入主内存的变量中。
把一个变量从主内存复制到工作内存时,必须顺序执行read 和 load ,
把一个变量从工作内存复制到主内存时,必须顺序执行store 和 write。
但是,不需要连续执行,他们之间可以插入其他的指令,
如 访问内存中a  , b  可能执行read a  read b load b load a。

volatile 变量特殊规则:
这个java 提供的轻量级同步机制,很难理解。
首先,一个volatile 变量具有两种属性:
1.保证变量对所有线程的可见性,当一个线程改变了,新值对其他线程是立即可见的,而普通变量,必须将值由工作线程传递给主内存。
很多人认为被volatile就能实现 并发的一致性,但是被volatile修饰的变量运算不一定是线程安全的,网友们一般都那自增来举例,同样,我们来看一个。

package cn.pmax;

public class VolatileTest {

	private static volatile int count = 0;

	public static void main(String[] args) {

		//开启100个线程 自增count
		for (int i = 0; i < 100; i++) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					try {
						Thread.sleep(20);			//延时20毫秒 增强实验效果
					} catch (InterruptedException e) {
					}

					count++;

				}
			}).start();
		}

		while (Thread.activeCount() > 1)<span style="white-space:pre">				//让所有开启线程都结束
			Thread.yield();

		System.out.println(count);

	}

}
可以看到 得到的结果并非是100,因为++在jvm编译后 生成了多条指令。

  static int access$008();

    Code:

       0: getstatic     #1                  // Field count:I

       3: dup           

       4: iconst_1      

       5: iadd          

       6: putstatic     #1                  // Field count:I

       9: ireturn     


我们可以看到,自增操作分成了两步(4,5)当执行到 4时 有其他线程进来操作了过期数据,使结果变小。
所以,volatile 变量职能保证可见行,但是不能保证原子行。

volatile多用于两种情况:
1.运算不依赖当前值,
2.并发时 作为 状态标签

两个常用例子:

volatile boolean asleep;
	while(!asleep) {
		//TODO ....
	}

当asleep状态发生改变时,while语句会立即终止。




饭后继续。。。。。


2.禁止指令重排序优化。
一个因优化引发的血案

public class Singleton {

	private static Singleton intance;

	private Singleton() {}

	public synchronized static Singleton getInstance() {
		if (intance == null) {
			intance = new Singleton();
		}
		return intance;
	}
}

因为 synchronized 加在方法上降低性能,所以有人提出了双重锁检查

if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null)
					instance = new Singleton();
			}
		}

貌似很完美的解决了性能问题,但是由于对jvm虚拟机不熟,引来了一个潜在的问题,可能导致 获得一个还没有完成的初始化对象。

我们JIT生成反汇编代码发现 new 一个对象时 分为三步,
memory = allocate();   //1:分配对象的内存空间
ctorInstance(memory);  //2:初始化对象
instance = memory;     //3:设置instance指向刚分配的内存地址
由于jvm会对指令重排序,所以可能 执行完成1后 执行 3 这时候还没有初始化对象,当一个线程到达最外层的if判断时,instance不为null 直接返回了一个没有完成初始化的对象。

所以jdk 5 后给 属性加volatile 即可 防止指令重排序来解决线程安全问题。


public class Singleton {

	private volatile static Singleton instance = null;

	private Singleton() {}

	public static Singleton getInstance() {

		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null)
					instance = new Singleton();
			}
		}
		return instance;

	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

© 著作权归作者所有

共有 人打赏支持
呆萌的我
粉丝 4
博文 15
码字总数 15443
作品 0
天津
私信 提问
JVM内存结构 VS Java内存模型 VS Java对象模型

Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、Java内存模型和...

Java架构
07/11
0
0
Java并发(1)- 聊聊Java内存模型

引言 在计算机系统的发展过程中,由于CPU的运算速度和计算机存储速度之间巨大的差距。为了解决CPU的运算速度和计算机存储速度之间巨大的差距,设计人员在CPU和计算机存储之间加入了高速缓存来...

knock_小新
07/18
0
0
基于JVM原理、JMM模型和CPU缓存模型深入理解Java并发编程

许多以Java多线程开发为主题的技术书籍,都会把对Java虚拟机和Java内存模型的讲解,作为讲授Java并发编程开发的主要内容,有的还深入到计算机系统的内存、CPU、缓存等予以说明。实际上,在实...

leoliu168
11/08
0
0
《成神之路-基础篇》JVM——JVM内存结构(已完结)

Java内存模型,Java内存管理,Java堆和栈,垃圾回收 本文是《成神之路系列文章》的第一篇,主要是关于JVM的一些介绍。 持续更新中 参考文章: Java虚拟机的内存组成以及堆内存介绍 Java堆和栈...

05/05
0
0
再有人问你Java内存模型是什么,就把这篇文章发给他!

前几天,发了一篇文章,介绍了一下JVM内存结构、Java内存模型以及Java对象模型之间的区别。有很多小伙伴反馈希望可以深入的讲解下每个知识点。Java内存模型,是这三个知识点当中最晦涩难懂的...

技术小能手
09/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

docker部署springboot项目

安装docker 菜鸟教程 springboot项目 maven依赖 <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001......

yimingkeji
今天
10
0
ios多个target

1.建立3个target,分别为heroone,heroone test,heroone dev;分别为正式环境,test环境,dev环境 2.注意取消掉autocreate以防止名字不对,分别以Duplicate的方式建立另外两个scheme 3.创建...

HeroHY
今天
5
0
php获取客户端IP

php获取客户端IP 首先先阅读关于IP真实性安全的文章:如何正確的取得使用者 IP? 「任何從客戶端取得的資料都是不可信任的!」 HTTP_CLIENT_IP头是有的,但未成标准,不一定服务器都实现。 ...

DrChenXX
昨天
0
0
. The valid characters are defined in RFC 7230 and RFC 问题

通过这里的回答,我们可以知道: Tomcat在 7.0.73, 8.0.39, 8.5.7 版本后,添加了对于http头的验证。 具体来说,就是添加了些规则去限制HTTP头的规范性 参考这里 具体来说: org.apache.tom...

west_coast
昨天
1
0
刷leetcode第704题-二分查找

今天双十一买的算法书到货了,路上刷到有人说的这个题,借(chao)鉴(xi)一下别人的思路,这个是C++标准库里面的经典方法,思路精巧,优雅好品味 int search(int* nums, int numsSize, in...

锟斤拷烫烫烫
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部