Java線程安全主要是由兩個特性來組成,一、原子性。二、可見性。java
原子性相似於數據庫事務中的原子性,一個操做必須善始善終,不能中途被中止。數據庫
而可見性的意思:是多個線程之間訪問共享變量時,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;
輸出結果:
能夠看到,程序已經如咱們所預期的,達到線程安全的效果了。
其實volatile關鍵字解決的僅僅是線程之間可見性的問題,換句話說,它僅僅的解決的是線程之間數據可否被讀取到,而沒有解決多線程的原子性問題,即讀到的數據是否是一個線程完整執行後的結果值。若是想保證多線程的原子性問題,能夠參考java下的concurrent包下提供了一些原子類,如tomicInteger、AtomicLong、AtomicReference等,能夠很好的解決變量數據的正確性問題。