1. 程式人生 > >4-4 執行緒安全性-可見性

4-4 執行緒安全性-可見性

一個執行緒對主記憶體的修改可以及時的被其他執行緒觀察到

導致共享變數線上程間不可見的原因

  • 執行緒交叉執行
  • 重排序結合線程交叉執行
  • 共享變數更新後的值沒有在工作記憶體與主存間及時更新

可見性之synchronized

JMM關於synchronized的規定

  • 執行緒解鎖前,必須把共享變數的最新值重新整理到主記憶體
  • 執行緒加鎖時,將清空工作記憶體中共享變數的值,從而使用共享變數時需要從主記憶體中重新讀取最新的值(加鎖與解鎖是同一把鎖)

可見性之volatile

通過加入記憶體屏障和禁止重排序優化來實現

  • 對volatile變數寫操作時,會在寫操作後加入一條store屏障指令,將本地記憶體中的共享變數值重新整理到主記憶體
  • 對volatile變數讀操作時,會在讀操作前加入一條load屏障指令,從主記憶體中讀取共享變數 在這裡插入圖片描述 在這裡插入圖片描述

用volatile做計數操作,看是否是執行緒安全的

package com.mmall.concurrency.example.count;

import com.mmall.concurrency.annoations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

@Slf4j
@NotThreadSafe
public class CountExample4 {

    // 請求總數
    public static int clientTotal = 5000;

    // 同時併發執行的執行緒數
    public static int threadTotal = 200;

    public static volatile int count = 0;

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
        /*
        執行結果:
         5000
        4992
        4998
        因此volatile也是執行緒不安全的
        原因是:假設開始時count=5,執行緒1和執行緒2同時做count++操作時,執行緒1和執行緒2都從記憶體中讀取到了count=5,然後執行緒1和執行緒2同時對count做加一操作,然後執行緒1把count的結果6由工作記憶體存回記憶體,執行緒2也把count的結果6由工作記憶體存回記憶體,因此執行結果小於等於5000
        */
    }

    private static void add() {
        count++;
        // 1、count
        // 2、+1
        // 3、count
    }
}

執行結果:

5000
4992
4998

因此volatile也是執行緒不安全的。 原因是:假設開始時count=5,執行緒1和執行緒2同時做count++操作時,執行緒1和執行緒2都從記憶體中讀取到了count=5,然後執行緒1和執行緒2同時對count做加一操作,然後執行緒1把count的結果6由工作記憶體存回記憶體,執行緒2也把count的結果6由工作記憶體存回記憶體,因此執行結果小於等於5000。

volatile使用

volatile適合使用在狀態標識的場景中,如下例項:(volatile還可以用於檢查2次的場景中)

volatile boolean inited = false;//全域性變數

//執行緒1:
context = loadContext();
inited= true;

// 執行緒2:
while( !inited ){
    sleep();
}
doSomethingWithConfig(context)