是否可以修改非易失性变量,以便另一个线程能够"看到"更新?

Pac*_*ier 4 java synchronization

我有一个Thread-X,它每秒读取一个非易失性变量,这样做没有任何同步手段.

现在我想知道是否有某种方法可以修改Thread-Y上的非易失性变量,以便Thread-Y的写入(最终)在Thread-X上可见?

public class Test {
    private static boolean double = 1; // this variable is
                                             // strictly not volatile

    public static void main(String args[]) {
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                while (true) {
                    System.out.println(variable);
                    try {
                        java.lang.Thread.currentThread().sleep(1000);
                    } catch (java.lang.InterruptedException e) {
                    }
                }
            }
        }).start(); // line 17
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                // the task is to change variable to "2" such the write
                // is (eventually) registered by the other threads

                // allowed to use any synchronization/locking techniques
                // required, but line 1 to line 17 must not be changed
            }
        }).start();
    }
}
Run Code Online (Sandbox Code Playgroud)

是否可以修改非易失性变量,以便在没有任何同步技术(原始读取)的情况下读取它的另一个线程能够最终"看到"更新?

背景:

我需要从大量线程中读取一个变量,无数次.

根据我的理解(纠正我,如果我错了),在大多数cpus(例如x86)读取volatile变量是"几乎完全免费"但不是"完全免费".

既然我有无数个线程的无限次读取,我希望变量是非易失性的.然而,一旦在蓝色的月亮中,变量需要更新.在我的用例中,更新该变量需要花费多少并不重要,但该更新最终必须由读取器线程读取.

解决方案:

基于Tomasz的评论,我已经构建了这个解决方案,我想知道解决方案-1是否有缺陷或者它是否可靠?

public class Solution1 {
    private static double variable = 1; // this variable is
                                        // strictly not volatile

    public static void main(String args[]) {
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                while (true) {
                    System.out.println(variable);
                    try {
                        java.lang.Thread.currentThread().sleep(1000);
                    } catch (java.lang.InterruptedException e) {
                    }
                }
            }
        }).start(); // line 17
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                variable = 2;
                // writer-thread now terminates,
                // is it guaranteed that when it
                // "terminates successfully", variable
                // is updated on the reader-thread ?
            }
        }).start();
    }
}
Run Code Online (Sandbox Code Playgroud)

基于Joonas的评论,我已经构建了这个解决方案,我想知道解决方案-2是否有缺陷或者它是否可靠?

public class Solution2 {
    private static double variable = 1; // this variable is
                                        // strictly not volatile
    private static volatile boolean lock = false;

    public static void main(String args[]) {
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                while (true) {
                    System.out.println(variable);
                    try {
                        java.lang.Thread.currentThread().sleep(1000);
                    } catch (java.lang.InterruptedException e) {
                    }
                }
            }
        }).start(); // line 17
        new java.lang.Thread(new java.lang.Runnable() {

            @Override
            public void run() {
                variable = 2;
                lock = false; // does this line guarantee
                                // that other threads will now 
                                // see the update to variable (piggypacking)?

                // now let's assume this thread doesn't terminate
            }
        }).start();
    }
}
Run Code Online (Sandbox Code Playgroud)

Joo*_*kka 5

是否可以修改非易失性变量,以便在没有任何同步技术(原始读取)的情况下读取它的另一个线程能够最终"看到"更新?

有些同步技术必须使用,因为否则(JIT)编译器被允许优化线System.out.println(false);(如果false是首先由该线程看到的值).也就是说,它可以优化读取变量.

我不知道它实际上有多大可能做到这一点,但根据Java内存模型可以,所以你的选择是:

  • volatile.这可能是您最简单,最轻的选择.
  • AtomicBoolean.在这里这里讨论它与易变性.
  • synchronized块.在这种情况下矫枉过正.
  • java.util.concurrent.locks.Lock.比以往更多的功能synchronized.
  • Thread.join().在这种情况下没用(读取线程会等待编写器终止).
  • 捎带.甚至不要考虑它.太多可能会出错的事情.

只需使用volatile并让JVM担心有效地实现它.它不贵.

  • 好.有一种称为"piggypacking"的技术,它基于以下事实:访问*any*volatile变量将*all*缓存变量的本地副本与主内存同步.因此,有时可以在没有同步的情况下写入一个变量,然后利用后续同步对另一个变量进行同步,以确保使用两个变量更新主存储器.[参见此参考](http://www.javamex.com/tutorials/synchronization_piggyback.shtml).它非常脆弱,不应该用于任何事情,但仅仅是因为...... (3认同)