简体   繁体   English

Java多线程与方法调用结合使用?

[英]Java multi-threading used in conjuction with method invocation?

I met the following Java class on the internet: 我在网上遇到了以下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);
}
}

Tried running this a lot of times, the result is almost always: 尝试多次运行,结果几乎总是:

     1000
     b=1000

In my original guess, I thought the first line should be "2000", since tt.m2() is just a method invocation(not a thread), the main method should continue with its execution and get the resulting "b" as the one has been assigned value 2000 in method m2. 在我最初的猜测中,我认为第一行应该是“ 2000”,因为tt.m2()仅仅是方法调用(不是线程),所以main方法应该继续执行并获得结果“ b”作为已在方法m2中为一个赋值2000。

The second try that I did is to uncomment out the 我所做的第二次尝试是取消注释

 System.out.println("m2") 

in m2 method.Suprisingly, the result will be nearly always: 令人惊讶的是,结果几乎总是:

 m2
 2000
 b=1000

Why adding a statement in the m2 method, will cause the output value of tt.b to be changed? 为什么在m2方法中添加一条语句,将导致tt.b的输出值被更改?

Sorry I am quite confused here about the difference between threads and method invocation, hope experts can help out! 抱歉,关于线程和方法调用之间的区别,我感到很困惑,希望专家可以帮忙!

Synchronization in the Java sense combines several things. Java意义上的同步结合了几件事。 In this case these points are interesting: 在这种情况下,这些要点很有趣:

  • mutual exclusion 互斥
  • memory barriers for readers 读者的记忆障碍
  • memory barriers for writers 作家的记忆障碍

After entering a synchronized block (or method) you have got two guarantees: You have the lock (mutual exclusion) and that the JVM and the compiler will discard any cache for the for the synchronization object . 输入synchronized块(或方法)后,您有两个保证:拥有锁(互斥),并且JVM和编译器将为同步对象丢弃任何缓存。 This means an access to this.b will fetch the actual value for 'b' from the RAM and not from any cache but only once . 这意味着对this.b的访问将从RAM中而不是从任何缓存中获取'b'的实际值, 而只会获取一次 Then it will work with the cached copy again. 然后它将再次与缓存的副本一起使用。

Leaving a synchronized block in turn guarantees that the CPU flushes all dirty (ie written) caches to the memory. 依次保留synchronized块可确保CPU将所有脏(即已写入)的高速缓存刷新到内存中。

The point in your stuff is: System.out.println(tt.b); 您的资料中的重点是: System.out.println(tt.b); is in no way synchronized which means the access to it has not crossed a defined memory barrier. 绝不同步,这意味着对它的访问尚未跨越定义的内存屏障。 So although the other thread has written a new value for b and flushed it to the RAM the main thread has no idea, that it should read b from RAM and not from its own cache. 因此,尽管另一个线程为b编写了一个新值并将其刷新到RAM,但主线程不知道,它应该从RAM而不是从其自己的缓存中读取b

The solution is: 解决方案是:

synchronized(tt){
    System.out.println(tt.b);
}

This meets the golden rule, that if something is synchronized then every access to it should be synchronized and not only half of the accesses. 这符合黄金法则,即如果某事物已同步,则对它的每个访问都应该同步,而不仅仅是访问的一半。

And regarding your added System.out : There are three things: 关于添加的System.out有三件事:

First: It is slow (compared to some memory fiddling). 第一:它很慢(相比于一些内存问题)。 This means that in the meantime the CPU or the JVM might decide for themselves, that a new look to tt might be appropriate 这意味着,在此期间,CPU或JVM可能会自行决定,以tt新外观是合适的

Second: It is big (compared to some memory fiddling). 第二:它很大(相比于一些内存摆弄)。 This means that the touched code alone might evict tt from the caches. 这意味着,仅触及代码可能驱逐tt从缓存。

Third: It is synchronized internally. 第三:内部同步。 This means that you crossed some memory barriers (which might have nothing to do with your tt - who knows). 这意味着您遇到了一些内存障碍(可能与您的tt无关-谁知道)。 But these might also have some effect. 但是这些可能也会产生一些影响。

This is the lead rule of multithreading debugging: Adding System.out in order to catch errors will, according to Murphy, actually hide the problem. 这是多线程调试的主要规则:根据Murphy的说法,添加System.out以捕获错误实际上会隐藏问题。

I guess this is JVM implementation specific. 我猜这是特定于JVM实现的。

Basically, each thread has its's own copy (view) of the object variables and the way they are synced back and forth is not determined. 基本上,每个线程都有自己的对象变量副本(视图),并且无法确定它们来回同步的方式。

The most likely cause is that System.out.println is slow. 最可能的原因是System.out.println缓慢。 The cause of the "unexpected" results is because of a race condition between the delay ( Thread.sleep ) and the overhead of opening the output stream ( System.out.println ). 结果“意外”的原因是由于延迟( Thread.sleep )与打开输出流的开销( System.out.println )之间存在争用条件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM