++操做的線程安全性及相關問題

衆所周知,++操做並非線程安全的。這篇文章主要講述其非線程安全的緣由以及相關問題。java

使用volatile修飾仍不是線程安全的緣由

i++分爲如下3步:安全

  1. 從內存中讀取到count
  2. count+1
  3. 將結果寫回內存

這3步每一步之間都是能夠被中斷的,加volatile只是保證從內存中讀取到的count值是最新的值,可是存在在別的線程中的count還未寫回主存的可能ide

例如:性能

  1. 線程A讀取到count爲10,此時線程中斷
  2. 線程B讀取到count也爲10,線程B進行++操做,結果爲11寫回主存,
  3. 此時線程A恢復,因爲它已經從內存中讀到count了,因此它仍會從10開始加,獲得11寫回主存。
  4. 咱們能夠發現,10++在線程A,B各自進行了一次

線程安全的寫法

加鎖

public class ThreadTest implements Runnable {
    int count = 0;

    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 100000; i++) {
                count++;
            }
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Runnable runnable = new ThreadTest();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        Future f1, f2;
        f1 = executorService.submit(runnable);
        f2 = executorService.submit(runnable);
        f1.get();
        f2.get();
        System.out.println(((ThreadTest) runnable).count);
        executorService.shutdown();
    }
}

使用原子類

原子類能夠的單一操做都是原子性的。它的實現並非依賴於加鎖而是使用CAS。this

CAS的基本原理以下:atom

從內存位置V中讀取值A,並根據A計算值B,而後再將值B寫回V。
可是寫回V以前,會檢查內存位置V的值是否等於A,若是不等於,就不會將值寫回V。而是從新進行一次上述操做。線程

public class ThreadTest2 implements Runnable {
    AtomicInteger count = new AtomicInteger();

    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 100000; i++) {
                count.getAndIncrement();
            }
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Runnable runnable = new ThreadTest2();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        Future f1, f2;
        f1 = executorService.submit(runnable);
        f2 = executorService.submit(runnable);
        f1.get();
        f2.get();
        System.out.println(((ThreadTest2) runnable).count.get());
        executorService.shutdown();
    }
}

性能問題

加鎖固然會必定程度上影響性能,可是正確性優於性能。code

使用java.util.concurrent.atomic中的原子類在不少狀況下都有着優於鎖的性能,可是在本例中並非如此。我認爲是由於compare比較錯誤次數太多,重複次數太多致使的。內存

相關文章
相關標籤/搜索