簡體   English   中英

有效的Java項目66:為什么要同步讀取和寫入方法?

[英]Effective Java Item 66: Why to synchronize both read and write methods?

在《有效的Java->項目66》中,Joshua強調需要同步讀取和寫入操作,以避免活動失敗。

在此示例中,我認為寫方法上的同步是多余的。 即使刪除了同步寫入方法,程序仍會運行並終止,沒有任何問題。 需要同步才能查看對象的一致狀態,這是通過“讀取時同步”方法實現的。

請讓我知道您對此的看法。

import java.util.concurrent.TimeUnit;

public class StopThread {

private static boolean stopRequested;

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

        @Override
        public void run() {
            int i = 0;
            while (!isStopRequested())
                i++;
        }
    }).start();
    ;
    TimeUnit.SECONDS.sleep(1);
    setStopRequested(true);
}

private static synchronized boolean isStopRequested() {
    return stopRequested;
}

private static void setStopRequested(boolean stopRequested) {
    StopThread.stopRequested = stopRequested;
}
}

您提到的示例可能最適合於演示在沒有同步(或易失性)的情況下如何無法保證何時將線程本地內存中的值刷新到主內存 ,但是此示例當然並不最適合於演示“讀寫並發問題”。

我認為您可能誤解了示例的目的,目的是顯示在沒有同步的情況下線程通信的效果。 閱讀以下摘錄自同一項目#66:

即使沒有同步,StopThread中同步方法的操作也是原子的。 換句話說,這些方法上的同步僅用於其通信效果,而不用於相互排斥。

您認為它起作用的原因是因為在沒有同步的情況下,當線程本地內存中的值將刷新到主內存時,JVM不會做出任何“ 保證 ”,這意味着它可能根本不會刷新,也可能會刷新但不能保證“何時”。 當您運行它時,值將被刷新,但是沒有必要總是將其刷新,因此這就是出現“保證”的地方,如果您使用同步(或易失性,取決於場景),那么JVM會保證“ 發生” -before關系,這只是保證從線程本地內存到主內存的值刷新將“發生在任何線程從主內存讀取值之前”。

在沒有同步的情況下檢查與讀寫相關的並發問題的影響的一個更好的示例可能是流行的銀行帳戶借方貸方示例,以下是快速示例:

public class AccountDebitCredit {

    private int accountBalance = 100;

    public static void main(String[] args) throws InterruptedException {
        final AccountDebitCredit accountDebitCredit = new AccountDebitCredit();

        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    // if you remove synchronization from t1 and t2, then there would be concurrency issues.
                    synchronized (accountDebitCredit) {
                        accountDebitCredit.accountBalance = accountDebitCredit.accountBalance + 100;
                    }
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    // if you remove synchronization from t1 and t2, then there would be concurrency issues.
                    synchronized (accountDebitCredit) {
                        accountDebitCredit.accountBalance = accountDebitCredit.accountBalance - 100;
                    }
                }
            }
        });

        System.out.println(accountDebitCredit.accountBalance);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(accountDebitCredit.accountBalance);
    }

}

我稍微修改了您的代碼,但現在根本無法完成。 當您不使用原子變量時,您將永遠不會發生下一步。 JIT可以優化您的讀/寫操作。

import java.util.concurrent.TimeUnit;

public class StopThread {

    private static boolean stopRequested = false;

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

            @Override
            public void run() {
                int i = 0;
                while (!stopRequested)
                    i++;
            }
        }).start();
        TimeUnit.SECONDS.sleep(10);
        stopRequested = true;
        System.out.println("Set to true");
        Thread.sleep(40 * 1000L);
    }
}

在我的機器上,此代碼永遠不會完成。 由於修改后的代碼幾乎可以完成相同的工作,因此很容易看出您所依賴的JIT行為將來可能會發生變化。 也許在下一個Java版本中,您的代碼也將無法完成。

暫無
暫無

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

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