JMM 用来定义程序中变量的访问规则,定义者,想要屏蔽掉不同的硬件和系统造成的内存访问差异。
之前了解的JMM空也曾提到工作内存的概念,每个线程都有自己的工作内存,所有的变量多存在主内存,工作内存存储的是各个线程用到的变量 主内存的副本拷贝,工作内存之间不能直接操作对方工作内存的变量,要通过主内存作为中间介,各个线程只能操作各自的工作线程变量,无法直接操作主内存变量。
然后就是就是定义了8中原子操作,用来控制变量,分别是 lock、unlock,read、load、use、assign、store、write。
定义了8个规则
1一个变量只能被一个线程lock,能被同一个线程多次lock,相应的要多次unlock,后面说的就是可重入。
2lock一个变量后,清除自己的工作内存先,再要使用时,重新从主内存加载;unlock后,要同步到主内存。
3变量只能定义在主内存,在使用user或者store前要load 或者assign
4工作内存的变量没变化,不能平白无故同步主内存,一旦改变一定要同步主内存。
以上是基础知识。摘自《深入了解java虚拟机》
再遇到两个线程同时操作一个对象的字段时,遇到了一些问题,
先贴代码
public class MyObject {
private String name="1";
private String pass="11";
public void print() {
System.out.println(name+" "+pass);
}
public void setvalue(String u,String p){
this.name=u;
if(Thread.currentThread().getName().equals("a")){
System.out.println("a停止 ");
Thread.currentThread().suspend();
}
this.pass=p;
}
}
public class Test {
public static void main(String[] args) throws Exception{
final MyObject object = new MyObject();
Thread thread2 = new Thread(){
@Override
public void run() {
while (true){
object.print();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread2.start();
Thread thread1 = new Thread(){
@Override
public void run() {
object.setvalue("a","aa");
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(500);
//thread2.start();
}
描述:thread2和Thread1 会同时去操作一个object对象,
thread2运行后,输出的 是1和11,在Thread1启动后,会改变object的字段值,这时候thread2循环输出的也变了,变成了a和11.
思考:
1每个线程都有自己的工作内存,会把object对象(字段为1,11)拷贝一份到thread2和Thread1的各自工作内存,第一次 thread2输出字段为(1,11)可以理解。
2但是当Thread1 启动后改变了期工作内存的object字段变成(a,11),
3接下来 thread2输出字段为也变成了 (a,11)
于是接在思考不是有各自的工作内存吗,线程thread1也没有停止啊,为什么线程thread2的工作内存变量也会变掉。
以下是自己的看法
线程thread1的suspend()方法会同步到主内存,还有sleep()也是如此。
然后接下来就是怎么 主内存同步到 线程thread2的工作内存中
这是别人见解
为了提升性能,线程里面有工作内存,这样访问数据不用去主存读取,可以快一些。共享变量被线程修改后,该线程的工作内存中的值就会和其他线程不一致,也和主存的值不一致,所以需要将工作内存的值刷入主存,但是这个刷入可能其他线程并没有看到。
使用 volatile 后可以通过 cpu 指令屏障强制要求读操作发生在写操作之后,并且其他线程在读取该共享变量时,需要先清理自己的工作内存的该值,转而重新从主存读取,volatile 保证一定会刷新,但是不写也不一定其他线程看不见。
就是不一定,有随机性,不加voliatile其他线程 也不一定看不见。加了一定看的见。
这就是目前我的理解。