1. 程式人生 > >Java併發程式設計--Volatile詳解

Java併發程式設計--Volatile詳解

摘要

     Volatile是Java提供的一種弱同步機制,當一個變數被宣告成volatile型別後編譯器不會將該變數的操作與其他記憶體操作進行重排序。在某些場景下使用volatile代替鎖可以減少程式碼量和使程式碼更易閱讀。 Volatile特性   1.可見性:當一條執行緒對volatile變數進行了修改操作時,其他執行緒能立即知道修改的值,即當讀取一個volatile變數時總是返回最近一次寫入的值   2.原子性:對於單個voatile變數其具有原子性(能保證long double型別的變數具有原子性),但對於i ++ 這類複合操作其不具有原子性(見下面分析) 使用volatile變數的前提
  1.對變數的寫入操作不依賴變數的當前值,或者能夠確保只有單一的執行緒修改變數的值   2.該變數不會與其他狀態變數一起納入不變性條件中   3.在訪問變數時不需要加鎖 volatile可見性 volatile的可見性正是基於happend -before(先行發生)關係實現的。 happend-before:java記憶體模型有八條可以保證happend-before的規則(詳見《深入理解Java虛擬機器》P376),如果兩個操作之間的關係無法從這八條規則中推匯出來的話,它們就沒有順序保障,虛擬機器就可以對它們隨意地進行重排序. 其中就包含”volatile變數規則“:對一個volatile變數的寫操作先行發生於後面對這個變數的讀操作,此規則保證虛擬機器不會對volatile讀/寫操作進行重排序。 通過一個例子來了解vloative的可見性 例1:
public class VolatileTest extends  Thread{
    private boolean isRunning = true;
    public boolean isRunning(){
        return isRunning;
    }
    public void setRunning(boolean isRunning){
        this.isRunning= isRunning;
    }
    public void run(){
        System.out.println("進入了run...............");
        while (isRunning){}
        System.out.println("isRunning的值被修改為為false,執行緒將被停止了");
    }
    public static void main(String[] args) throws InterruptedException {
        VolatileTest volatileThread = new VolatileTest();
        volatileThread.start();
        Thread.sleep(1000);
        volatileThread.setRunning(false);   //停止執行緒
    }
}
輸出:
進入了run...............
發現並沒有輸出”isRunning的值被修改為為false,執行緒將被停止了”這一句,說明通過setRunning來修改isRunning的值對於該程式是不可見的,也就是說程式不知道自己的值被修改了,為什麼?      原因:Java記憶體模型(JMM)規定了所有的變數都儲存在主記憶體中,主記憶體中的變數為共享變數,而每條執行緒都有自己的工作記憶體,執行緒的工作記憶體儲存了從主記憶體拷貝的變數,所有對變數的操作都在自己的工作記憶體中進行,完成後再重新整理到主記憶體中,回到例1,第18行號程式碼主執行緒(執行緒main)雖然對isRunning的變數進行了修改且有重新整理回主記憶體中(《深入理解java虛擬機器》中關於主記憶體與工作記憶體的互動協議提到變數在工作記憶體中改變後必須將該變化同步回主記憶體
),但volatileThread執行緒讀的仍是自己工作記憶體的舊值導致出現多執行緒的可見性問題,解決辦法就是給isRunning變數加上volatile關鍵字。       當變數被宣告成volatile型別後,執行緒對該變數進行修改後會立即重新整理回主記憶體,而其他執行緒讀取該變數時會先將自己工作記憶體中的變數置為無效,再從主記憶體重新讀取變數到自己的工作記憶體,這樣就避免發生執行緒可見性問題。 volatile記憶體語義總結如下 1.當執行緒對volatile變數進行寫操作時,會將修改後的值重新整理回主記憶體 2.當執行緒對volatile變數進行讀操作時,會先將自己工作記憶體中的變數置為無效,之後再通過主記憶體拷貝新值到工作記憶體中使用。 volatile原子性    volatile並不完全具有原子性,對於複合操作其仍存線上程不安全的問題,如 例2
public class VolatileTest1{
    private volatile  int value;  //將value變數宣告成volatile型別
    public  void increment(){
         value ++;   
         System.out.println(value);   
    }
    public static void main(String[] args) {
        final VolatileTest1 volatileTest1 = new VolatileTest1();
        for(int i = 0; i < 10; i ++){
            new Thread(new Runnable() {
                public void run() {
                     volatileTest1.increment();
                }
            }).start();
        }
    }
}
輸出:
1
6
5
4
2
3
8
7
9
10
執行緒每次對value進行自增操作,顯然輸出結果不是我們想要的那種,這裡就出現了執行緒安全問題,為什麼?   像value ++這樣的操作並不具有原子性,其實際的過程如下: 當執行緒1在步驟2對value進行計算時,剛好其他執行緒也對value進行了修改,這時執行緒1返回的值就不是我們期望的值了,於是出現執行緒安全問題,所以volatile不能保證複合操作具有原子性;解決辦法就是給increment方法加鎖(lock/synchronized)或將變數宣告為原子類型別。 Synchronized與volatile區別  1.volatile只能修飾變數,而synchronized可以修改變數,方法以及程式碼塊 2.volatile在多執行緒中不會存在阻塞問題,synchronized會存在阻塞問題 3.volatile能保證資料的可見性,但不能完全保證資料的原子性,synchronized即保證了資料的可見性也保證了原子性 4.volatile解決的是變數在多個執行緒之間的可見性,而sychroized解決的是多個執行緒之間訪問資源的同步性

相關推薦

Java併發程式設計--Volatile

摘要      Volatile是Java提供的一種弱同步機制,當一個變數被宣告成volatile型別後編譯器不會將該變數的操作與其他記憶體操作進行重排序。在某些場景下使用volatile代替鎖可以減少程式碼量和使程式碼更易閱讀。 Volatile特性   1.可見性:當一條執行緒對volatile

Java 併發程式設計工具類 CountDownLatch

CountDownLatch是一個在java1.5被引入同步工具類,它允許一個或多個執行緒一直等待,直到其他執行緒的操作執行完後再執行。countdownlatch在Java開發中應用場景及其廣泛,同時也是面試中的高頻考點。每一個Java程式設計師都應該熟練掌握,在本篇文章中,我將會從以下幾方面對其

Java併發程式設計wait(), notify(),sleep())

        上一篇部落格,重點講解了java中鎖的機制,省的在多執行緒之間出現混亂的局面,其實主要能夠理解鑰匙即可。如果要保證方法之間能夠獨立完全的執行,因此就必須所有的方法都共用一把鑰匙。然後小

Java併發工具類

在JDK的併發包裡提供了幾個非常有用的併發工具類。CountDownLatch、CyclicBarrier和Semaphore工具類提供了一種併發流程控制的手段,Exchanger工具類則提供了線上程間交換資料的一種手段。本章會配合一些應用場景來介紹如何使用這些工具類。 等待多執行緒完成的Cou

Java併發程式設計——volatile

引 volatile可以看成是輕量級的低配版的Synchronized,他主要是作用於共享變數,保證共享變數的可見性。確保共享變數在主記憶體中一致地準確的更新通知到各個執行緒,這是Volatile的可見性,同時由於它是低配版的Synchronized,所以他也沒有了Synchronized的

併發程式設計模型

       現在的處理器使用寫緩衝區臨時儲存向記憶體寫入資料。寫緩衝區可以保證指令流水線持續執行,他可以避免與處理器停頓下來等待向記憶體寫入資料而產生的延遲。同時,通過一批處理的方式重新整理寫緩衝區,以及合併寫緩衝區中對同一個記憶體地址的多次寫,減少對記憶體匯流排的佔用。雖

Java併發之Semaphore

        一、入題        Semaphore是一種基於計數的訊號量。它可以設定一個閾值,基於此,多個執行緒競爭獲取許可訊號,做完自己的申請後歸還,超過閾值後,執行緒申請許可訊號將會被阻塞。Semaphore可以用來構建一些物件池,資源池之類的,比如資料庫連線池,

Java併發程式設計volatile關鍵詞的解析

volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。 volatile關鍵字雖然從字面上理解起來比較簡單,但是要用好

Java併發之AQS

一、概述   談到併發,不得不談ReentrantLock;而談到ReentrantLock,不得不談AbstractQueuedSynchronizer(AQS)!   類如其名,抽象的佇列式的同步器,AQS定義了一套多執行緒訪問共享資源的同步器框架,許多同步類實現都依賴於它,如常用的Reentrant

java併發程式設計:volatile關鍵字解析

volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。 volatile關鍵字雖然從字面上理解起來比較

java併發程式設計 -volatile關鍵字

java語言提供了一種稍弱的同步機制,即volatile變數,用來確保將變數的更新操作通知到其他的執行緒。 當把變數宣告為volatile型別後,編譯器與執行時都會注意到這個變數是共享的,因此不會將該變數上的操作與其他記憶體操作一起重排序,volatile變

Java併發——Executor框架(Executor框架結構與框架成員)

一、什麼是Executor框架?我們知道執行緒池就是執行緒的集合,執行緒池集中管理執行緒,以實現執行緒的重用,降低資源消耗,提高響應速度等。執行緒用於執行非同步任務,單個的執行緒既是工作單元也是執行機制,從JDK1.5開始,為了把工作單元與執行機制分離開,Executor框架

Java併發:ThreadLocal

前言ThreadLocal的作用是提供執行緒內的區域性變數,這種變數在多執行緒環境下訪問時能夠保證各個執行緒裡變數的獨立性。ThreadLocal無論在專案開發還是面試中都會經常碰到,本文就ThreadLocal的使用、主要方法原始碼詳解、記憶體洩漏問題展開討論。1.基本使用

Java 併發程式設計 : volatile 關鍵字解析

volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。 volatile關鍵字雖然從字面上理解起來比較簡單,但是要用好不是一件容易的事情。由於v

Java併發程式設計---volatile關鍵字與atomic原子類

一.Volatile關鍵字      1.1 概念            volatile關鍵字的主要作用是使變數在多個執行緒間可見    1.2 示例程式 package com.thread.

Java併發程式設計-volatile變數

  併發程式設計時,Java 的volatile提供了一種弱的執行緒同步機制。 volatile提供了兩種語義:   1)禁止編譯器對其修飾的程式碼進行重組   2)將變數的寫操作立即同步到記憶體中而

Java併發程式設計---volatile關鍵字解析

volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。 volatile關鍵字雖然從字面上理解起來比較簡

java併發程式設計-volatile關鍵字

volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。 volatile關鍵字雖然從字面上理解起來比較簡單,但是要用好不是一件容易的事情。由

Java併發之CompletionService

CompletionService是什麼? 它是JUC包中的一個介面類,預設實現類只有一個ExecutorCompletionService。   CompletionService幹什麼的? 它將非同步任務的生成和執行結果的處理進行了解耦,用來執行Callable的任務(實際也是通過Executo

【搞定Java併發程式設計】第8篇:volatile關鍵字

上一篇:Java記憶體模型詳解:https://blog.csdn.net/pcwl1206/article/details/84871090 目  錄: 1、volatile的作用 1.1、volatile的可見性 1.2、volatile禁止指令重排序 2、vola