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其他線程 也不一定看不見。加了一定看的見。
這就是目前我的理解。