繁体   English   中英

Java volatile变量和同步获取器

[英]Java volatile variables and synchonized getters

我正在用Java并发与Java“实践中的并发”一书进行一些实验。

我还有一个问题:同步的getter方法是否等于volatile私有变量? 一些代码:

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);

}
}

这个变体给我“ from class:”消息。

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

在代码执行方面有什么区别? 我知道“ synchonized”表示“一个线程访问”,而volatile表示“从主线程获取值而不进行缓存”

但是为什么两个变体的行为是相同的?

同步getter方法是否等于volatile私有变量?

没有。

等价于synchronized setter和synchronized getter与volatile private变量。

如果使用synchronized getter和普通的(非同步的)setter,则一个线程的写操作与另一个线程的(后续)变量读操作之间将不存在先于关系。

另一点是,getter和setter需要在同一件事上进行同步。

最后,getter或setter可能比读或写变量要复杂得多。 在那种情况下, synchronized构造可确保相对于在同一锁上同步的其他线程原子地执行所有操作。 相比之下, volatile变量不能给您原子性保证。


但是,为什么两个变体的行为相同?

你真是幸运!

Java内存模型说, 保证读取操作可以看到先前读取操作的结果。 如果以正确的方式进行操作,则应用程序将正确运行。

相反,情况并非如此。 如果你这样做不对,你可能会得到正确的行为,但无论如何也不能保证(!):

  • 您可能总是在特定平台上获得它。 例如具有给定数量的内核和给定的内存架构的硬件。

  • 您可以始终使用给定版本的Java来获得它。

  • 否则,您可能会得到正确的行为……除非在罕见或异常情况下。

但是,如果您不执行Java内存模型所需的操作,则无法保证行为。


有几个后果:

  • 测试不会证明您正确的并发代码是正确的。 充其量它可能表明它是不正确的。

  • 测试以证明不正确的并发代码不正确也很困难。 您可能会发现您的错误代码似乎可以正常工作。

只有释放并随后获取锁才能建立事前关系。 因此,您需要使用synchronized方法来获取和设置变量ready值。

但是在您的示例中, ready volatile就足够了,因为可以为变量设置新值,并从变量获取值也要建立在before-before之前

最主要的是,您需要保持一致。 如果对变量使用synchronized方法,则仅应通过那些synchronized方法对变量进行所有访问。

您的问题中的示例将始终可预测地使用volatile但只有将setter标记为“ synchronized 时,才会发生

ready = true;

在主线程中

while (!isReady()) 

在第二个线程。 因此,JVM内存模型不能保证此处没有任何内容。 它可能会按您期望的方式工作,而不是您所期望的方式工作。

并发的问题是您需要降低对确定性和可预测性的期望。 当您执行单线程操作时,计算机就是这样做的。 现在您正在处理并发,它将不再需要。 您不能问“为什么这样或那样”或“为什么这样或那样”? 您所要问的是是否有保证。

当您使用自己的工具版本在计算机上尝试使用这两种代码时,它们会产生相同的结果,因为没有强制要求它们会表现出不同的行为。 没有任何规定要求他们会做同样的事情。 这些代码会发生某些事情,您可以预测其中的一些,而不能预测其余的。

无论如何,与同步方法或volatile变量的区别在于,可以保证对volatile变量的任何访问,读取或写入,就好像只有一个主内存而线程本地内存缓存不存在一样。 同步方法仅在您输入同步方法时才这样做。 在这里,您仅在读取变量时进行同步,并且在写入时未进行任何适当的处理。

这意味着您的带有同步的示例有很多机会无法给出预期的结果。 并不是说它一定会失败。 也许会,也许不会。 我们所能说的就是正确编程所能保证的。 我们无法确定您的编程未知后会发生什么。

暂无
暂无

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

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