簡體   English   中英

Java Volatile,同步,原子示例

[英]Java Volatile ,synchronization,atomic example

嗨,我正在實踐中閱讀Java並發性,並且閱讀了有趣的聲明,指出

鎖定可以保證可見性和原子性。 volatile變量只能保證可見性。

誰能解釋一下,如果將一個變量聲明為volatile,所有其他讀取線程都將獲得更新的值,那么為什么我要關心語句中的原子性,如: counter = counter + 1 ;

提前致謝。

volatile關鍵字的影響大約是該變量上的每個讀寫操作都是原子的。

但是,值得注意的是,需要多個讀/寫操作(例如,i ++相當於i = i + 1,可以進行一次讀和一次寫操作)不是原子操作,因為另一個線程可能會向i寫入數據。在讀取和寫入之間。

像AtomicInteger和AtomicReference這樣的Atomic類在原子上提供了各種各樣的操作,特別是包括AtomicInteger的增量。

這就是為什么您需要關心諸如counter = counter + 1之類的語句中的原子性的原因

請檢查此帖子揮發性與原子性

這是一個自包含的示例可執行應用程序,演示了僅憑volatile是不夠的。 四個線程將每個計數器遞增10,000次,因此您希望計數器最后為40,000。 它使用原始的int變量和AtomicInt,並分別嘗試5次。

import java.util.Collections;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

class AtomicDemo {
    interface Demo extends Callable<Void> {
        int getCounter();
    }

    static class UsePrimitive implements Demo {
        private volatile int counter = 0;

        public Void call() throws Exception {
            for (int i = 1; i <= 10000; ++i) {
                ++counter;
            }
            return null;
        }

        public int getCounter() {
            return counter;
        }
    }

    static class UseAtomic implements Demo {
        final AtomicInteger counter = new AtomicInteger(0);

        public Void call() throws Exception {
            for (int i = 1; i <= 10000; ++i) {
                counter.incrementAndGet();
                System.out.print("");
            }
            return null;
        }

        public int getCounter() {
            return counter.get();
        }
    }

    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newFixedThreadPool(4);
        for (int i = 1; i <= 5; ++i) {
            Demo demo = new UsePrimitive();
            exec.invokeAll(Collections.nCopies(4, demo));
            System.out.println("Count to 40000 using primitive, attempt number " + i + ": " + demo.getCounter());
        }
        for (int i = 1; i <= 5; ++i) {
            Demo demo = new UseAtomic();
            exec.invokeAll(Collections.nCopies(4, demo));
            System.out.println("Count to 40000 using atomic, attempt number " + i + ": " + demo.getCounter());
        }
        exec.shutdownNow();
    }
}

典型輸出:

Count to 40000 using primitive, attempt number 1: 39711
Count to 40000 using primitive, attempt number 2: 39686
Count to 40000 using primitive, attempt number 3: 39972
Count to 40000 using primitive, attempt number 4: 39840
Count to 40000 using primitive, attempt number 5: 39865
Count to 40000 using atomic, attempt number 1: 40000
Count to 40000 using atomic, attempt number 2: 40000
Count to 40000 using atomic, attempt number 3: 40000
Count to 40000 using atomic, attempt number 4: 40000
Count to 40000 using atomic, attempt number 5: 40000

您會看到,只有使用AtomicInt才能始終獲得預期的結果。

暫無
暫無

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

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