(一)java内存区域概况
jvm运行java程序时把所管理的内存分成几个部分:方法区、java栈、本地方法栈、java堆、pc程序计数器。
class字节码装载解析后,在多线程环境中,方法区和java堆数据共享,每个线程自带pc程序计数器和java栈,栈帧中包含方法的所有状态(局部变量、传参、返回值、运算中间结果等)。对共享数据需要考虑多线程并发问题。
更详细内容可参考《深入理解JVM虚拟机》。
(二)计算机系统原型
1.内存和处理器速度不同,缓存的出现,导致数据不同步
多线程都有缓存备份,回写可能会导致数据错误
2.处理器和编译器对代码和指令的优化,导致运算乱序
只要不影响运算结果,处理器可以调整代码执行顺序,多线程操作中会导致数据错误
(三)JMM概况
JMM(java momery model)提供了线程进行通讯时,确保互斥性和可见性。
(1)解决内存可见性问题
共享数据放主内存中,每个线程有自己的工作内存。
线程A读取数据时,先从主内存拷贝数据到工作内存,进行运算后再从工作内存回写到主内存,线程间无法相互访问对方的数据,通过主内存实现通讯。
JMM可以控制指定共享数据实现内存可见性,写入主内存时,同步更新其他线程中的变量值,如volatile变量的使用。
(2)禁止重排序
编译器优化代码,不改变运算结果的情况下,可以实现代码重排序;处理器执行指令,不改变运算结果的情况下,可以实现指令重排序。
JMM可以禁止编译器代码重排序和处理器执行指令重排序(通过加入内层屏蔽)。
多线程并发问题代码剖析
/**
* 代码重排序demo
*
* @author peter_wang
* @create-time 2015-1-8 下午3:06:02
*/
public class CodeChangeDemo {
private static boolean isChange = false;
private static class ChangeThread
extends Thread {
public void run() {
System.out.println("ChangeThread start"); //1
isChange = true; //2
};
};
private static class GetChangeThread
extends Thread {
public void run() {
System.out.println("GetChangeThread start");
if (isChange) { //3
isChange = false;
System.out.println("change success");
}
else {
System.out.println("change no success");
System.exit(0);
}
};
};
/**
* @param args
*/
public static void main(String[] args) {
while (true) {
ChangeThread changeThread = new ChangeThread();
GetChangeThread getChangeThread = new GetChangeThread();
changeThread.start();
getChangeThread.start();
// 防止线程过多,等执行完上面俩线程再继续
while (Thread.activeCount() > 1) {
}
}
}
}
运行结果:
ChangeThread start
GetChangeThread start
change success
ChangeThread start
GetChangeThread start
change success
ChangeThread start
GetChangeThread start
change no success
进程退出可能原因:
1.线程ChangeThread中代码顺序调整,1和2调换,执行顺序1-3-2
2.线程ChangeThread执行完,工作内存中的isChange没有回写回主内存,导致3中isChange不是最新值。
参考文献:
2.http://ifeve.com/java-memory-model-1/