Kev*_*vin 0 java concurrency multithreading
我在互联网上遇到了以下Java类:
public class Lock1 implements Runnable {
int b=100;
public synchronized void m1() throws Exception {
b=1000;
Thread.sleep(50);
System.out.println("b="+b);
}
public synchronized void m2() throws Exception {
Thread.sleep(30);
//System.out.println("m2");
b=2000;
}
public void run() {
try {m1();}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Lock1 tt=new Lock1();
Thread t = new Thread(tt);
t.start();
tt.m2();
System.out.println(tt.b);
}
}
Run Code Online (Sandbox Code Playgroud)
尝试运行这么多次,结果几乎总是:
1000
b=1000
Run Code Online (Sandbox Code Playgroud)
在我最初的猜测中,我认为第一行应该是"2000",因为tt.m2()只是一个方法调用(不是一个线程),main方法应该继续执行并得到结果"b"作为在方法m2中为一个赋值2000.
我做的第二次尝试是取消注释掉
System.out.println("m2")
Run Code Online (Sandbox Code Playgroud)
在m2方法中.结果几乎总是如此:
m2
2000
b=1000
Run Code Online (Sandbox Code Playgroud)
为什么在m2方法中添加语句会导致tt.b的输出值发生变化?
对不起,我在这里很困惑线程和方法调用之间的区别,希望专家能帮帮忙!
Java意义上的同步结合了几个方面.在这种情况下,这些要点很有趣:
输入synchronized块(或方法)后,您有两个保证:您具有锁定(互斥),并且JVM和编译器将丢弃同步对象的任何高速缓存 .这意味着访问this.b将从RAM中获取"b"的实际值,而不是从任何缓存中获取,但只获取一次.然后它将再次使用缓存副本.
synchronized反过来保留一个块可以保证CPU将所有脏的(即写入的)高速缓存刷新到内存中.
你的东西中的要点是:System.out.println(tt.b);绝不同步,这意味着对它的访问没有超过定义的内存障碍.因此,虽然另一个线程已经为bRAM 写入了一个新值并将其刷新到RAM,但是主线程不知道它应该b从RAM 读取而不是从它自己的缓存中读取.
解决方案是:
synchronized(tt){
System.out.println(tt.b);
}
Run Code Online (Sandbox Code Playgroud)
这符合黄金法则,如果某些东西是同步的,那么每次访问它都应该同步,而不仅仅是访问的一半.
关于你的补充System.out:有三件事:
第一:它很慢(与一些内存摆弄相比).这意味着在此期间,CPU或JVM可能会自行决定新的外观tt可能是合适的
第二:它很大(与一些记忆力相比).这意味着单独触摸的代码可能会tt从缓存中逐出.
第三:内部同步.这意味着你克服了一些记忆障碍(这可能与你的tt- 谁知道无关).但这些也可能会产生一些影响.
这是多线程调试的主要规则:System.out根据Murphy的说法,添加以捕获错误将实际隐藏问题.
| 归档时间: |
|
| 查看次数: |
99 次 |
| 最近记录: |