【问题标题】:Java8: How volatile field work with lambdaJava8:volatile 字段如何与 lambda 一起使用
【发布时间】:2017-02-26 05:51:35
【问题描述】:

我是 Java8 和多线程工作的新手。我在下面尝试了这段代码

public class Test {
    public static boolean bchanged = true;

    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    if (bchanged != bchanged) {
                        System.out.println("here");
                    }
                }
            }
        }
        ).start();

        new Thread((Runnable) () -> {
            while (true) {
                bchanged = !bchanged;
            }
        }).start();
    }
}

当我运行这段代码时,没有“这里”的打印。但是,当我改变

public static volatile boolean bchanged = true;

然后“这里”将被打印出来。

我最初的推论是,lambda 将具有布尔值的本地副本,并且当它不是 volatile 时它不会影响另一个线程,但是当我尝试在两个线程中打印出布尔值时,它证明我错了。所以在这种情况下我很困惑,volatile 如何影响 lambda 的工作方式。

【问题讨论】:

  • Lambda 与 volatile 字段无关。请阅读这个问题:stackoverflow.com/questions/4885570/…
  • 当其他人说你的问题不是关于 lambdas 时,他们试图告诉你 lambda 表达式只是定义匿名内部类的简写方式,并且创建该类的实例。类或实例没有什么特别之处:它们遵循与定义的内部类和以普通方式创建的实例完全相同的规则。在您的情况下,内部类实现了Runnable,但Runnable 也没有什么神奇之处。您所询问的魔法之所以发生,是因为您的代码由多个线程执行。
  • 捕获规则正是为了避免这种混淆:如果你可以修改变量,lambda表达式(或内部类)不能通过保持旧值的不变副本来实现。因此,字段可以更改并且不按值捕获,而 局部变量 按值捕获并且必须保持不变,请阅读(有效)final

标签: java multithreading java-8 race-condition volatile


【解决方案1】:

这不是关于 lambda,而是关于在多个线程中访问共享变量。您需要使用synchronized、锁、AtomicBooleanvolatile 或其他一些线程安全替代方案。使用您编写的代码,编译器可能会缓存bchanged 的值。它不知道有另一个线程在修改它,所以第一个线程看到一个陈旧的缓存值。

【讨论】:

  • 感谢您回答问题。我可以在这里跟进一个问题。如果“bchanged”不是易失的,线程1是否有可能在评估if条件时清理缓存并再次读取“bchanged”的修改值?
  • 是的,这是可能的。行为是不可预测的。它可能每次都重新读取变量。它可能永远不会重新阅读它。它有时可能会重新阅读它,但其他没有可辨别模式的人则不会。您不想编写具有不可预测行为的代码。最重要的一点是它有可能从不重新读取该值。
  • @LIP17:如果使用正确,任何指定用于建立“happens-before”关系的构造都可以充当“清理缓存”操作。这需要两个线程合作。如果将bchanged 声明为volatile,则两个线程都会看到volatile 变量,因此使用相同的机制。吹毛求疵,虽然它会在大多数系统上完成你所期望的,但它仍然不是一个正式正确的程序。由于第一个线程包含一个没有任何等待状态的无限循环,因此无法保证第二个线程会运行。
【解决方案2】:

如其他答案中所述, volatile 和 lambda 无关。
Volatile 正在打印“here”,因为变量“bchanged”的值未缓存为“Java 中的 volatile 关键字用作 Java 编译器和线程的指示符,它们不缓存此变量的值并始终从主内存中读取它” .
您可以使用以下链接了解有关 volatile 的更多信息。
阅读更多:http://javarevisited.blogspot.com/2011/06/volatile-keyword-java-example-tutorial.html#ixzz4ZlvIJYzZ
编程洞穴:https://www.youtube.com/watch?v=_aNO6x8HXZ0&t=113s

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多