深入理解java虚拟机(6)---内存模型与线程 & Volatile
其实关于线程的使用,之前已经写过博客讲解过这部分的内容:
http://www.cnblogs.com/deman/category/621531.html
JVM里面关于多线程的部分,主要是多线程是如何实现的,以及高效并发。
1.Java内存模型
CPU在运行的时候,不可能把所有的东西都放在寄存器里面,所有需要使用内存。这个内存就是我们知道的那个内存。
但是实际情况是,内存的读写速度于CPU的指令操作差了几个数量级。所以为了跟高效的使用CPU,就有高速缓存这么一个东西。
以下是Intel 酷睿i7 6700K参数:
三级缓存8MB。
百度以下就知道这个“三级缓存”是个神马东西。
而java的内存模型与物理结构非常相识,有一个主内存,对应我们计算机的内存,还有每个线程都有一个工作内存,对应于高速缓存。

可以看到,每个java线程都有自己独立的内存。
这也就解释了,为什么不同线程,如果不同步的话,变量就会有并发的问题。
这里关于工作内存和主内存的拷贝问题,是由JVM实现的,并不是正真意义上的内存复制。
2.内存间操作
1)lock,作用于主内存变量,把一个变量标记为线程独占。
2)unlock,与lock正相反。
3)read,作用于主内存变量,它把一个变量从主内存传输到工作内存中。
4)load,作用于工作内存变量,把从read里面获取的变量放入工作内存的变量副本中。
5)use,作用于工作内存变量,把变量的值传递给执行引擎。
6)assign,作用于工作内存变量,把执行引擎的值 复制给工作内存变量。同use相反
7)store,作用于工作内存变量,把工作内存变量传输到主内存中。
8)write,作用于主内存变量,把store获取的值,写入到住内存中的变量。
read & load, store & write成对出现。
还有其他一些规则,目的就是保证内存变量的 操作合理,有序。
3.并发编程的三个概念
1)原子性
计一个操作要么全部执行,要么不执行,不能被打断。
jvm通过lock & unlock指令来保证代码的原子性。反映到java代码就是synchronized.
2)可见性
可见性是指当一个程序修改变量以后,其他程序可以立即获得这个修改的值。
3)有序性
JVM在编译java代码,优化的时候,会重现排布java代码的顺序。但是会保证结果时候java代码的顺序结果一致的。
public Runnable mRun1 = new Runnable() {
@Override
public void run() {
int a = readFileName();
writeFile(a);
initialized = true;
}
};
上面readFileName 和initialized = true;没有必然关系,所以在实际执行的时候,可能会先执行initialized = true;
对于这个线程内的结果没有影响。
但是如果是多线程的情况下:
public Runnable mRun2 = new Runnable() {
@Override
public void run() {
while (!initialized)
{
try {
TraceLog.i("sleep");
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
doSomeThing(context);
}
};
initialized = true的执行顺序对线程2的结果有直接的影响。所有有序性在这种情况下,需要保证。
一般java里面用synchronized就可以保证。但是过多的synchronized会对性能有很大的损失。
4.volatile关键字
volatile关键字修饰后的变量.有2个作用:
1)用来确保将变量的更新操作通知到其他线程,保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新.
当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的.
但是volatile 不能保证线程是安全的,因为java里面的运算并非原子操作。2)volatile还有一个特性就是保证指令不重新排序。现在编译器,为了优化代码,都会重新排序指令。如果在多个线程里面,就会有很大的问题。
但是指令重排是JVM在它认为合理的情况下做的,所以很难模拟出这一情况。
boolean aBoolean = false;
public Runnable mRun1 = new Runnable() {
@Override
public void run() {
aBoolean = false;
while (!aBoolean)
{
doSomeThing();
}
}
}; public Runnable mRun2 = new Runnable() {
@Override
public void run() {
aBoolean = true;
}
};
只是2个线程的例子,线程2用来关闭线程1.一般情况下,它会运行良好,但是有小概率情况下,会有问题。
aBoolean 在赋值为true的时候,没有立刻被同步到主内存,而这时候线程1的工作内存aBoolean 的拷贝是false。
所以会陷入死循环。
volatile关键字就可以避免这种情况的发生。
1)当aBoolean = true;发生后,线程2会立即把aBoolean 的值更新到主内存。
2)线程1在使用到aBoolean 是,会首先到主内存重新获取新的值。然后更新工作内存中的值,这个时候 aBoolean就是true了,循环退出。
5.volatile 保证原子操作吗?
volatile不能保证线程是安全的。
package com.joyfulmath.jvmexample.multithread; import com.joyfulmath.jvmexample.TraceLog; import java.util.concurrent.CountDownLatch; /**
* @author deman.lu
* @version on 2016-05-26 14:34
*/
public class VolatileTest2 {
public volatile int inc = 0;
static CountDownLatch countDownLatch = new CountDownLatch(10);
public void increase() {
inc++;
} public static void main() {
TraceLog.i();
final VolatileTest2 test = new VolatileTest2();
for(int i=0;i<200;i++){
new Thread(){
@Override
public void run() {
super.run();
for(int j=0;j<50;j++)
test.increase(); countDownLatch.countDown();
// TraceLog.i(String.valueOf(Thread.currentThread().getId()));
}
}.start();
} try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(test.inc);
TraceLog.i(String.valueOf(test.inc));
}
}
05-26 14:48:06.060 15209-15209/com.joyfulmath.jvmexample I/System.out: 9950
05-26 14:48:06.061 15209-15209/com.joyfulmath.jvmexample I/VolatileTest2: main: 9950 [at (VolatileTest2.java:41)]
结果并不是10000,原因就是 自增函数不是原子操作,而Volatile只能保证数值是更新到住内存,但是,当线程1执行过程中假设inc=5,线程2可能已经获取了inc的值。
这个时候,线程1,++以后变为6,线程2也是6,而且因为主内存的值 & 线程2的值一致,就不会触发其他线程无效的情况,所以线程3取到的值,还是6.所有这个数值的结果是无法确认的,但是<10000.
But, 我在android23下编译,发现一直是10000.不清楚原因???
6.volatile的有序性
volatile只能保证部分有序性,比如说:
volatile boolean initialized = false;
public void run() {
context = readFileName();
writeFile(context);
initialized = true;
play();
Drawable();
}
上面,3,4两行语句顺序是乱序的,6,7也是,但是5 一定在3,4之后运行。 也就是5的执行为止不变,而且,3,4 不能和6,7互换执行顺序。这就是volatile有限的有序性。
参考:
http://www.cnblogs.com/dolphin0520/p/3920373.html
《深入理解java虚拟机》周志明
深入理解java虚拟机(6)---内存模型与线程 & Volatile的更多相关文章
- java虚拟机10.内存模型与线程
多任务处理在现代计算机操作系统中是一项必备的功能,让计算机同时去做几件事情,不仅是因为计算机的运算能力强大了,更重要的原因是计算机的运算速度与它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘 ...
- 深入理解JAVA虚拟机(内存模型+GC算法+JVM调优)
目录 1.Java虚拟机内存模型 1.1 程序计数器 1.2 Java虚拟机栈 局部变量 1.3 本地方法栈 1.4 Java堆 1.5 方法区(永久区.元空间) 附图 2.JVM内存分配参数 2.1 ...
- Java虚拟机:内存模型详解
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实 ...
- java虚拟机的内存模型
一.为什么要了解java虚拟机的内存模型 java虚拟机作为java代码运行的平台,是java技术的基石.了解java虚拟机的内存模型也就变得十分必要.它能帮助我们更好的了解java代码的运行机制,更 ...
- Java虚拟机—Java8内存模型(整理版)
1.概述 对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要手动释放内存,不容易出现内存泄露和内存溢出问题.一旦出现内存泄露和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,排查错误 ...
- [深入理解Java虚拟机]<自动内存管理>
Overview 走近Java:介绍Java发展史 第二部分:自动内存管理机制 程序员把内存控制的权利交给了Java虚拟机,从而可以在编码时享受自动内存管理.但另一方面一旦出现内存泄漏和溢出等问题,就 ...
- 深入理解java虚拟机【内存溢出实例】
通过简单的小例子程序,演示java虚拟机各部分内存溢出情况: (1).java堆溢出: Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收 ...
- 深入理解JAVA虚拟机 自动内存管理机制
运行时数据区域 其中右侧三个一起的部分是每个线程一份,左侧两个是所有线程共享的. 程序计数器(Program Counter Register) 英文名称叫Program Counter Regist ...
- 深入理解Java虚拟机02--Java内存区域与内存溢出异常
一.概述 我们在进行 Java 开发的时候,很少关心 Java 的内存分配等等,因为这些活都让 JVM 给我们做了.不仅自动给我们分配内存,还有自动的回收无需再占用的内存空间,以腾出内存供其他人使用. ...
随机推荐
- Mysql学习笔记(十三)权限管理
学习内容: 1.权限管理: 关于mysql的权限简单的理解就是mysql允许你做你权利以内的事情,不可以越界.比如只允许你执行select操作,那么你就不能执行update操作.只允许你从某台机器上连 ...
- 参数嗅探(Parameter Sniffing)(1/2)
这个问题会在参数话的SQL语句(例如存储过程)与SQL Server里的计划缓存机制结合的时候会出现.这个文章分为2个部分,第1部分会介绍下参数嗅探(Parameter Sniffing)的概况,第2 ...
- android和ios,音频互通方案
好久不更新博客上,从年前从公司辞职,这半年以来,一直靠做一些外包app养活自己!也算是达成了自己年前制定的目标!可是也想着总不能一直做外包吧,所以决定做一些自己觉得有意思的app,挂到应用商店上和ap ...
- 【UWP】对 Thickness 类型属性进行动画
好几个月没写 blog 了,一个是在忙新版的碧影壁纸,另一方面是等(观望)周年更新的 api(不过现在还是比较失望,仍然没法支持矩形以外的 Clip).闲话少说,进入主题. 在 UWP 中,出于性能考 ...
- MVC怎么在当前视图中,传递参数给到另外一个视图?
在TransData.cshtml视图中: <div> <!--在一个视图中,请求另外一个视图,并且将数据传到另外一个视图--> <!--视图中调用无返回值的方法,需要加 ...
- jQuery点缩略图显示大图片
2015年繁忙的一月份,无更多时间去学习ASP.NET MVC程序,二月份又是中国的新年,长达半个月的假期,望回到老家中,在无电脑无网络的日子里,能有更多时间陪伴年迈的父母亲. 今天学习jQuery的 ...
- 自定义控件开发的调试及DesignMode的状态处理
在开发Winform程序的时候,我们往往需要根据需要做一些自定义的控件模块,这样可以给系统模块重复利用,或者实现更好的效果等功能.但在使用的时候,我们又往往设计时刻发现一些莫名其妙的错误,那么我们该如 ...
- WPF后台设置xaml控件的样式System.Windows.Style
WPF后台设置xaml控件的样式System.Windows.Style 摘-自 :感谢 作者: IT小兵 http://3w.suchso.com/projecteac-tual/wpf-zhi ...
- ACCESS作为网站数据库的弊端
现在网上绝大多数网站都是ACCESS+ASP的形式,因为ACCESS结构简单容易处理,而且也能满足多数的网站程序要求. ACCESS是小型数据库,既然是小型就有他根本的局限性,以下几种情况下数据库基本 ...
- x3dom 1.6 发布
X3DOM 库的1.6版本发布了,以下是最重要的一些变化: 完整的新的文档频道 - http://doc.x3dom.org x3dom实例频道 - http://examples.x3dom.or ...