简体   繁体   English

Java volatile变量和同步获取器

[英]Java volatile variables and synchonized getters

I am doing some experiments with java concurrency with book Java "concurrency in practise". 我正在用Java并发与Java“实践中的并发”一书进行一些实验。

I have next question: Is the syncronized getter method equals with volatile private variable? 我还有一个问题:同步的getter方法是否等于volatile私有变量? Some code: 一些代码:

public class ConsoleApp {

private static boolean ready;
private static int number;



public synchronized static boolean isReady() {
    return ready;
}



private static class ReaderThread extends Thread {
    public void run() {
        while (!isReady()) {
            //Thread.yield();  // jvm updates variables on sleeping thread sometimes 
        }
        System.out.println("from class: " + number);
    }
}



public static void main ( String [] arguments ) throws InterruptedException
{
    System.out.println("start");
    Thread.sleep(3000);

    ready = false;
    number = 23;

    System.out.println("inited variable");
    Thread.sleep(3000);

    new ReaderThread().start();

    System.out.println("thread started");
    Thread.sleep(3000);

    number = 42;
    ready = true;

    System.out.println("variables changed");
    Thread.sleep(3000);


    System.out.println("ended;: " + number);
    Thread.sleep(8000);
    System.out.println("end" + number);

}
}

This variant give me "from class: " message. 这个变体给我“ from class:”消息。 Like as

private static volatile boolean ready;
...
while (!ready) {
    ...
}

What is the difference in terms of code execution? 在代码执行方面有什么区别? I know that "synchonized" means "one thread access", and volatile means "get value from main thread without caching" 我知道“ synchonized”表示“一个线程访问”,而volatile表示“从主线程获取值而不进行缓存”

But why is the behavior of two variants is same?? 但是为什么两个变体的行为是相同的?

Is the synchronized getter method equals with volatile private variable? 同步getter方法是否等于volatile私有变量?

No. 没有。

The equivalence is with a synchronized setter AND a synchronized getter versus a volatile private variable. 等价于synchronized setter和synchronized getter与volatile private变量。

If you use a synchronized getter, and a plain (non-synchronized) setter, there will be no happens-before relationship between one thread's write and another thread's (subsequent) read of the variable. 如果使用synchronized getter和普通的(非同步的)setter,则一个线程的写操作与另一个线程的(后续)变量读操作之间将不存在先于关系。

The other point is that the getter and setter need to synchronize on the same thing. 另一点是,getter和setter需要在同一件事上进行同步。

Finally, a getter or setter may do something more complicated than reading or writing a variable. 最后,getter或setter可能比读或写变量要复杂得多。 In that case, the synchronized construct ensures that all of the actions are performed atomically with respect to other threads synchronizing on the same lock. 在那种情况下, synchronized构造可确保相对于在同一锁上同步的其他线程原子地执行所有操作。 By contrast, a volatile variable cannot give you an atomicity guarantee. 相比之下, volatile变量不能给您原子性保证。


But why is the behavior of two variants is same? 但是,为什么两个变体的行为相同?

You got lucky! 你真是幸运!

The Java Memory Model says when a read operation is guaranteed to see the result of a previous read operation. Java内存模型说, 保证读取操作可以看到先前读取操作的结果。 If you do it the right way your application will behave correctly. 如果以正确的方式进行操作,则应用程序将正确运行。

The converse is not the case. 相反,情况并非如此。 If you do it wrong, you may get the correct behavior anyway(!) but it is not guaranteed: 如果你这样做不对,你可能会得到正确的行为,但无论如何也不能保证(!):

  • You may always get it on a particular platform; 您可能总是在特定平台上获得它。 eg hardware with a given number of cores and given memory architecture. 例如具有给定数量的内核和给定的内存架构的硬件。

  • You may always get it with a given version of Java. 您可以始终使用给定版本的Java来获得它。

  • Or you may get the correct behavior ... except in rare or unusual circumstances. 否则,您可能会得到正确的行为……除非在罕见或异常情况下。

But there are no behavioral guarantees if you don't do what the Java Memory Model requires. 但是,如果您不执行Java内存模型所需的操作,则无法保证行为。


A couple of consequences: 有几个后果:

  • Testing won't prove that your correct concurrent code is correct. 测试不会证明您正确的并发代码是正确的。 At best, it may show that it is incorrect. 充其量它可能表明它是不正确的。

  • Testing to prove that incorrect concurrent code is incorrect is also difficult. 测试以证明不正确的并发代码不正确也很困难。 You may find that your incorrect code seems to work correctly. 您可能会发现您的错误代码似乎可以正常工作。

Only releasing and subsequently acquiring lock establishes happens-before relation. 只有释放并随后获取锁才能建立事前关系。 So you need to use synchronized method for both getting and setting value fro a variable ready . 因此,您需要使用synchronized方法来获取和设置变量ready值。

But in your example making ready volatile is enough as setting new value fro variable and getting value from variable also establish happens-before . 但是在您的示例中, ready volatile就足够了,因为可以为变量设置新值,并从变量获取值也要建立在before-before之前

The main thing is that you need to be consistent. 最主要的是,您需要保持一致。 If you use synchronized methods for variable then all access to that variable should be done via those synchronized methods only. 如果对变量使用synchronized方法,则仅应通过那些synchronized方法对变量进行所有访问。

Example in your question will always work predictably with volatile but with only setter marked as synchronized there is no happens-before between 您的问题中的示例将始终可预测地使用volatile但只有将setter标记为“ synchronized 时,才会发生

ready = true;

in main thread and 在主线程中

while (!isReady()) 

in second thread. 在第二个线程。 So JVM memory model guarantees nothing here. 因此,JVM内存模型不能保证此处没有任何内容。 It might work as you expect it might work not the way you expect. 它可能会按您期望的方式工作,而不是您所期望的方式工作。

The thing with concurrency is that you need to lower your expectations of certainty and predictability. 并发的问题是您需要降低对确定性和可预测性的期望。 The computer was doing that when you were doing things single-thread. 当您执行单线程操作时,计算机就是这样做的。 Now that you're dealing with concurrency it won't. 现在您正在处理并发,它将不再需要。 You can't ask "why are this or that alike" or "why are this or that different"? 您不能问“为什么这样或那样”或“为什么这样或那样”? All you can ask is whether something is ensured, or not ensured. 您所要问的是是否有保证。

The two versions of code happen to give the same results when you try it on your computer using your versions of your tools, because nothing mandate that they will behave differently. 当您使用自己的工具版本在计算机上尝试使用这两种代码时,它们会产生相同的结果,因为没有强制要求它们会表现出不同的行为。 Nothing mandates either that they will do the same thing. 没有任何规定要求他们会做同样的事情。 Something will happen with these codes, and you can predict some of it and not the rest. 这些代码会发生某些事情,您可以预测其中的一些,而不能预测其余的。

Anyway, the difference with synchronized method or volatile variable, is that any access, read or write, to a volatile variable is guaranteed to be made as if there was only one main memory and thread local memory caches didn't exist. 无论如何,与同步方法或volatile变量的区别在于,可以保证对volatile变量的任何访问,读取或写入,就好像只有一个主内存而线程本地内存缓存不存在一样。 While synchronized methods only do that when you enter the synchronized method. 同步方法仅在您输入同步方法时才这样做。 Here you're only synchronizing on reading the variable, and you're not doing any proper handling on writing. 在这里,您仅在读取变量时进行同步,并且在写入时未进行任何适当的处理。

That means your example with synchronize, had a lot of chances to fail to give the expected results. 这意味着您的带有同步的示例有很多机会无法给出预期的结果。 Not that it was guaranteed to fail. 并不是说它一定会失败。 Maybe it will, maybe it won't. 也许会,也许不会。 All we can tell is what is guaranteed by proper programming. 我们所能说的就是正确编程所能保证的。 We can't tell what will happen with what your programming left unknown. 我们无法确定您的编程未知后会发生什么。

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

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