簡體   English   中英

Java 中的 volatile 變量是否應該在第二個線程上不可見?

[英]Should volatile variables in Java not be visible on second thread?

我有一個帶有兩個線程AB的小示例應用程序。 兩者都在每個 volatile 變量上設置一個值之前花費一些 CPU 周期( A設置x的值, B設置y的值),然后B也打印出這兩個變量。 當這個游戲重復多次時,有時x的值在B可見,但有時不可見(即使xy都是易變的)。 這是為什么?

public class Vol2 {

    public static volatile int x = 0;
    public static volatile int y = 0;

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 50; i++) {

            x = 0;
            y = 0;


            Thread t1 = new Thread(() -> {
                doWork();
                x = 5;
            });

            Thread t2 = new Thread(() -> {
                doWork();
                y = 6;
                System.out.println("x: " + x + ", y: " + y);
            });

            t1.start();
            t2.start();
            t1.join();
            t2.join();

        }
    }

    public static void doWork() {
        int s = 0;
        for (int i = 0; i < 1_000_000; i++) {
            s += i;
        }
    }
}

線程t1t2可以同時執行。 有沒有保證, t1將指派x之前t2已分配y和讀取x

像這樣試試。 分配變量,然后做一些工作。 這使每個線程都有機會設置該值。 線程總是花費有限的時間來啟動和異步啟動,因此無法預測開始時的事件。

public class Vol2 {

    public static volatile int x = 0;
    public static volatile int y = 0;

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 50; i++) {

            x = 0;
            y = 0;


            Thread t1 = new Thread(() -> {
                x = 5;
                doWork();
            });

            Thread t2 = new Thread(() -> {
                y = 6;
                doWork();
                System.out.println("x: " + x + ", y: " + y);
            });

            t1.start();
            t2.start();
            t1.join();
            t2.join();

        }
    }

    public static void doWork() {
        int s = 0;
       try {
           Thread.sleep(10);
       } catch (InterruptedException ie){}
    }
}

你的例子有一些微妙的問題。 首先是doWork可能完全沒用,它沒有任何副作用,而JIT可以完全消除它。 想一想:既然那個循環純粹是一個沒有人看到的本地事物,那么首先為什么要這樣做?

然后,您對Thread::start有某種錯誤的理解,這個答案已經向您解釋了。 t1.start(); 安排一個線程啟動,並不意味着它會在t2.start();之前完成(甚至啟動t2.start(); .

然后,至少恕我直言,您使用了錯誤的工具來完成這項工作。 您需要jcstress ,這是專家為此類事情量身定制的工具。 這是您的代碼使用它的樣子:

@JCStressTest
@State
@Outcome(id = "0, 0", expect = Expect.FORBIDDEN, desc = "can not happen")
@Outcome(id = "0, 6", expect = Expect.ACCEPTABLE, desc = "writerY only")
@Outcome(id = "5, 6", expect = Expect.ACCEPTABLE, desc = "both")
public class TwoThreads {


    volatile int x = 0;
    volatile int y = 0;

    @Actor
    void writerX(II_Result r) {
        x = 5;
    }

    @Actor
    void writerY(II_Result r) {
        y = 6;
        r.r1 = x;
        r.r2 = y;
    }

}

如何運行它以及它意味着什么 - 對你來說是一個練習,但主要的一點是運行它只會給你兩種可能的結果:

5, 6 --> meaning writerX finished its work before writerY

或轉換為您的代碼, t1之前t2

0, 6 --> meaning writerY finished its work before writerX

因此, writerX x = 5丟失且從未記錄。 或者,轉換為您的代碼, t2t1之前完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM