JAVA的内存模型(变量的同步)
一个线程中变量的修改可能不会立即对其他线程可见,事实上也许永远不可见。
在代码一中,如果一个线程调用了MyClass.loop(),将来的某个时间点,另一个线程调用了MyClass.setValue(100),第一个线程可能仍然不会终止,可能永远循环下去
代码一:
public class MyClass{
private static final String TAG="MyClass";
private static int mValue=;
public static void setValue(int n){
mValue=n;
}
public static void loop(){
while(mValue!=){
try{
Log.i(TAG,"Value is "+mValue);
Thread.sleep();
}catch(Exception e){ }
}
}
}
上面的问题有两种解决办法:
一是使用synchronized关键字
public class MyClass{
private static final String TAG="MyClass";
private static int mValue=;
public static synchronized void setValue(int n){
mValue=n;
}
public static synchronized int getValue(){
return mValue;
}
public static void loop(){
int value;
while((value=getValue())!=){
try{
Log.i(TAG,"Value is "+value);
Thread.sleep();
}catch(Exception e){
}
}
}
}
二是使用volatile关键字
public class MyClass{
private static final String TAG="MyClass";
private static volatile int mValue=;//添加volatile关键字
public static void setValue(int n){
mValue=n;//如果是mValue+=n,因为不是原子操作,还得使用synchronized
}
public static void loop(){
while(mValue!=){
try{
Log.i(TAG,"Value is "+mValue);
Thread.sleep();
}catch(Exception e){
}
}
}
}
JAVA的同步机制就是围绕原子性和可见性两方面来确保线程安全的。
volatile关键字能保证数据的可见性,但是不能保证原子性,synchronized可以保证原子性,也可以间接保证可见性,因为他会将私有内存和公共内存中的数据做同步。
volatile关键字的作用就是当线程访问某一变量的时候,强制性从公共堆栈中读取,而不是从线程的私有堆栈中读取。一般来说栈是私有的,堆是公有的。
value++不是原子的,value=1是原子的。volatile关键字只可以解决变量声明是原子的那些并发问题,如果变量不是原子的就必须使用synchronized关键字
对于字符串:
其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
Java代码
- String s1 = "china";
- String s2 = "china";
- String s3 = "china";
- String ss1 = new String("china");
- String ss2 = new String("china");
- String ss3 = new String("china");

这里解释一下黄色这3个箭头,对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。这也就是有道面试题:String s = new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有”xyz”,就是两个。
对于基础类型的变量和常量:
变量和引用存储在栈中,常量存储在常量池中。
如以下代码:
Java代码
- int i1 = 9;
- int i2 = 9;
- int i3 = 9;
- public static final int INT1 = 9;
- public static final int INT2 = 9;
- public static final int INT3 = 9;

对于成员变量和局部变量:
成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。
形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。
如以下代码:
class BirthDate {
private int day;
private int month;
private int year;
public BirthDate(int d, int m, int y) {
day = d;
month = m;
year = y;
}
省略get,set方法………
}
public class Test{
public static void main(String args[]){
int date = ;
Test test = new Test();
test.change(date);
BirthDate d1= new BirthDate(,,);
}
public void change1(int i){
i = ;
}
}
对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:
1. main方法开始执行:int date = 9;
date局部变量,基础类型,引用和值都存在栈中。
2. Test test = new Test();
test为对象引用,存在栈中,对象(new Test())存在堆中。
3. test.change(date);
i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。
4. BirthDate d1= new BirthDate(7,7,1970);
d1 为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。 day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。
5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。
方法区:
不要在synchronized块中调用另一个对象的方法(它可能已经被锁住并等待之前用到的对象解锁),除非你能保证不会发生死锁,通常情况下只有亲手写其他对象的类的代码才敢确定不会发生死锁。
synchronized块中的wait()会自动释放锁,sleep()方法不释放锁,notify()执行后不会立即释放锁,在其所在同步synchronized块中代码执行完之后才会释放锁。
调用notify()方法一次只会随机通知一个线程进行唤醒,notifyAll()可以唤醒所有线程。wait(long)在long时间内等待是否有线程对其进行唤醒,如果没有,超过long时间则会自动唤醒。
JAVA的内存模型(变量的同步)的更多相关文章
- java线程内存模型,线程、工作内存、主内存
转自:http://rainyear.iteye.com/blog/1734311 java线程内存模型 线程.工作内存.主内存三者之间的交互关系图: key edeas 所有线程共享主内存 每个线程 ...
- Java虚拟机--内存模型与线程
Java虚拟机--内存模型与线程 高速缓存:处理器要与内存交互,如读取.存储运算结果,而计算机的存储设备和处理器的运算速度差异巨大,所以加入一层读写速度和处理器接近的高速缓存来作为内存和处理器之间的缓 ...
- 全网最硬核 Java 新内存模型解析与实验单篇版(不断更新QA中)
个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判.如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 i ...
- Java虚拟机内存模型及垃圾回收监控调优
Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...
- 掌握Java的内存模型,你就是解决并发问题最靓的仔
摘要:如果编写的并发程序出现问题时,很难通过调试来解决相应的问题,此时,需要一行行的检查代码,这个时候,如果充分理解并掌握了Java的内存模型,你就能够很快分析并定位出问题所在. 本文分享自华为云社区 ...
- Java对象内存模型
2 Java对象内存模型 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header). 实例数据(Instance Data)和对齐填充(Padding). 在 JVM ...
- Java线程内存模型-JVM-底层原理
public class Demo1 { private static boolean initFlag=false; public static void main(String[] args) t ...
- java String 内存模型
关于java的内存模型,参照以下的一篇文章: https://isudox.com/2016/06/22/memory-model-of-string-in-java-language/
- DirectX11 With Windows SDK--29 计算着色器:内存模型、线程同步;实现顺序无关透明度(OIT)
前言 由于透明混合在不同的绘制顺序下结果会不同,这就要求绘制前要对物体进行排序,然后再从后往前渲染.但即便是仅渲染一个物体(如上一章的水波),也会出现透明绘制顺序不对的情况,普通的绘制是无法避免的.如 ...
- Java的内存模型
"让计算机并发执行若干个运算任务"与"更充分地利用计算机处理器的效能"之间的因果关系,看起来顺理成章,实际上它们之间的关系并没有想象中的那么简单,其中一个重要的 ...
随机推荐
- Python高手之路【六】python基础之字符串格式化
Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This ...
- 通俗易懂的来讲讲DOM
DOM是所有前端开发每天打交道的东西,但是随着jQuery等库的出现,大大简化了DOM操作,导致大家慢慢的“遗忘”了它的本来面貌.不过,要想深入学习前端知识,对DOM的了解是不可或缺的,所以本文力图系 ...
- System.FormatException: GUID 应包含带 4 个短划线的 32 位数(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)。
在NHibernate数据库查询中出现了这个错误,由于是数据库是mysql的,当定义的字段为char(36)的时候就会出现这个错误. [解决方法] 将char(36) 改成varchar(40)就行了 ...
- .Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整(续)-使用配置文件动态注入
上次实现了依赖注入,但是web项目必须要引用业务逻辑层和数据存储层的实现,项目解耦并不完全:另一方面,要同时注入业务逻辑层和数据访问层,注入的服务直接写在Startup中显得非常臃肿.理想的方式是,w ...
- [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程
怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html ...
- 原生javascript 固定表头原理与源码
我在工作中需要固定表头这个功能,我不想去找,没意思.于是就写了一个,我写的是angularjs 自定义指令 起了个 "fix-header" ,有人叫 "freeze- ...
- JS图片上传预览插件制作(兼容到IE6)
其实,图片预览功能非常地常见.很意外,之前遇到上传图片的时候都不需要预览,也一直没有去实现过.现在手上的项目又需要有图片预览功能,所以就动手做了一个小插件.在此分享一下思路. 一.实现图片预览的一些方 ...
- 开源一款简单清爽的日历组件,JavaScript版的
源码会在最后给出地址,需要的朋友自己去下载.最近项目需要做一个日程安排的功能,就是点击日历的某一天弹出一个录入页面,填完信息后保存当天的日程安排.有日程的日期会有不同的标记(比如加一个背景色啥的).网 ...
- 【JavaScript】javascript中伪协议(javascript:)使用探讨
javascript:这个特殊的协议类型声明了URL的主体是任意的javascript代码,它由javascript的解释器运行. 比如下面这个死链接: <a href="javasc ...
- JAVA装饰者模式(从现实生活角度理解代码原理)
装饰者模式可以动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活. 该模式的适用环境为: (1)在不影响其他对象的情况下,以动态.透明的方式给单个对象添加职 ...