Java多線程 線程安全之volatile

Java線程安全主要是由兩個特性來組成,一、原子性。二、可見性。java

 

1.volatile關鍵字

原子性相似於數據庫事務中的原子性,一個操做必須善始善終,不能中途被中止。數據庫

而可見性的意思:是多個線程之間訪問共享變量時,A線程所修改的變量須要及時的被B線程或其餘全部線程所讀取到安全

直接上代碼,下面先看看當一個典型的多線程同步時的問題。多線程

public class RunThread implements Runnable {
    private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

    @Override
    public void run() {
        System.out.println("進入run了");
        while (isRunning) {

        }
        System.out.println("線程被中止了!");
    }

    public static void main(String[] args) throws InterruptedException {
        RunThread thread = new RunThread();
        new Thread(thread).start();
        Thread.sleep(100);
        thread.setRunning(false);
        System.out.println("已經賦值爲false");
    }
}

輸出結果:ide

根據代碼,咱們在main線程中已經設置isRunning爲false,子線程thread應該跳出while的死循環,打印出「線程被中止了!」,而結果爲何沒有呢?this

這就引出了JMM 內存模型的主存和內存的關係問題,先看下JMM 內存模型的示意圖。spa

这里写图片描述

能夠看到,每一個線程都擁有一個獨立的本地內存(也能夠叫工做內存),而下面大的主內存則是由全部線程所共享的,其實每次線程中所讀取到的變量都是從主存中得來的,而後寫入到本身的工做內存而後進行操做線程

基於這個模型,咱們其實就能夠回答上面的問題,在子線程中咱們每次讀取isRunning,都是從本地內存中,全部取得值一直是false,而在主線程中咱們設置了isRunning爲true是在主存中,子線程中沒有及時的讀取到主線程中改變的isRunning變量。這就形成了一個線程之間共享變量沒有及時同步,也能夠說是一個線程改變的值,沒有及時被其餘線程所看到。這裏咱們就引入了volatile關鍵字,只要被它修飾的變量在進行修改時,會向總線(BUS)發送消息,其餘線程讀取到該變量時,會獲得最新的值。簡單的來講,若是想要某個變量被全部線程讀取到最新的值,就使用volatile關鍵字進行修飾。code

因此在上面,咱們僅僅須要在isRunning前面加上volatile關鍵字修飾,以下:blog

private volatile boolean isRunning = true;

輸出結果:

能夠看到,程序已經如咱們所預期的,達到線程安全的效果了。

2.volatile的侷限性

其實volatile關鍵字解決的僅僅是線程之間可見性的問題,換句話說,它僅僅的解決的是線程之間數據可否被讀取到,而沒有解決多線程的原子性問題,即讀到的數據是否是一個線程完整執行後的結果值。若是想保證多線程的原子性問題,能夠參考java下的concurrent包下提供了一些原子類,如tomicInteger、AtomicLong、AtomicReference等,能夠很好的解決變量數據的正確性問題。

相關文章
相關標籤/搜索