1. 程式人生 > >Java併發——執行緒同步Volatile與Synchronized詳解

Java併發——執行緒同步Volatile與Synchronized詳解

0. 前言

轉載請註明出處:http://blog.csdn.net/seu_calvin/article/details/52370068

面試時很可能遇到這樣一個問題:使用volatile修飾int型變數i,多個執行緒同時進行i++操作,這樣可以實現執行緒安全嗎?提到執行緒安全、執行緒同步,我們經常會想到兩個關鍵字:volatile和synchronized,那麼這兩者有什麼區別呢?

1. volatile修飾的變數具有可見性

volatile是變數修飾符,其修飾的變數具有可見性。

可見性也就是說一旦某個執行緒修改了該被volatile修飾的變數,它會保證修改的值會立即被更新到主存,當有其他執行緒需要讀取時,可以立即獲取修改之後的值。

在Java中為了加快程式的執行效率,對一些變數的操作通常是在該執行緒的暫存器或是CPU快取上進行的,之後才會同步到主存中,而加了volatile修飾符的變數則是直接讀寫主存。

例子請檢視下面的3.1,幫助理解。

2. volatile禁止指令重排 

volatile可以禁止進行指令重排。

指令重排是指處理器為了提高程式執行效率,可能會對輸入程式碼進行優化,它不保證各個語句的執行順序同程式碼中的順序一致,但是它會保證程式最終執行結果和程式碼順序執行的結果是一致的。指令重排序不會影響單個執行緒的執行,但是會影響到執行緒併發執行的正確性。

程式執行到volatile修飾變數的讀操作或者寫操作時,在其前面的操作肯定已經完成,且結果已經對後面的操作可見,在其後面的操作肯定還沒有進行。

例子請檢視下面3.2,幫助理解。

3.  synchronized 

synchronized可作用於一段程式碼或方法,既可以保證可見性,又能夠保證原子性。

可見性體現在:通過synchronized或者Lock能保證同一時刻只有一個執行緒獲取鎖然後執行同步程式碼,並且在釋放鎖之前會將對變數的修改重新整理到主存中。

原子性表現在:要麼不執行,要麼執行到底。

例子請檢視下面3.3,幫助理解。

2. 總結
(1)從而我們可以看出volatile雖然具有可見性但是並不能保證原子性。

(2)效能方面,synchronized關鍵字是防止多個執行緒同時執行一段程式碼,就會影響程式執行效率,而volatile關鍵字在某些情況下效能要優於synchronized。

但是要注意volatile關鍵字是無法替代synchronized關鍵字的,因為volatile關鍵字無法保證操作的原子性。

3. volatile與synchronized的使用場景舉例(結合第1部分進行理解學習)

3.1 volatile的使用舉例

class MyThread extends Thread {           
    private volatile boolean isStop = false;        
    public void run() {    
        while (!isStop) {    
            System.out.println("do something");    
        }    
    }    
    public void setStop() {    
        isStop = true;    
    }          
}  


執行緒執行run()的時候我們需要線上程中不停的做一些事情,比如while迴圈,那麼這時候該如何停止執行緒呢?如果執行緒做的事情不是耗時的,那麼只需要使用一個標誌即可。如果需要退出時,呼叫setStop()即可。這裡就使用了關鍵字volatile,這個關鍵字的目的是如果修改了isStop的值,那麼在while迴圈中可以立即讀取到修改後的值。

如果執行緒做的事情是耗時的,那麼可以使用interrupt方法終止執行緒 。如果在子執行緒“睡覺”時被interrupt,那麼子執行緒可以catch到InterruptExpection異常,處理異常後繼續往下執行。

3.2 volatile的使用舉例

//執行緒1:
context = loadContext();   //語句1  context初始化操作
inited = true;             //語句2
 
//執行緒2:
while(!inited ){
  sleep()
}
doSomethingwithconfig(context);


因為指令重排序,有可能語句2會在語句1之前執行,可能導致context還沒被初始化,而執行緒2中就使用未初始化的context去進行操作,導致程式出錯。

這裡如果用volatile關鍵字對inited變數進行修飾,就不會出現這種問題了。

3.3 必須使用synchronized而不能使用volatile的場景

public class Test {
    public volatile int inc = 0;
    public void increase() {
        inc++;
    }
     
    public static void main(String[] args) {
        final Test test = new Test();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                };
            }.start();
        }
         
        while(Thread.activeCount()>1)  //保證前面的執行緒都執行完
            Thread.yield();
        System.out.println(test.inc);
    }
}


例子中用new了10個執行緒,分別去呼叫1000次increase()方法,每次執行結果都不一致,都是一個小於10000的數字。自增操作不是原子操作,volatile 是不能保證原子性的。回到文章一開始的例子,使用volatile修飾int型變數i,多個執行緒同時進行i++操作。比如有兩個執行緒A和B對volatile修飾的i進行i++操作,i的初始值是0,A執行緒執行i++時剛讀取了i的值0,就切換到B執行緒了,B執行緒(從記憶體中)讀取i的值也為0,然後就切換到A執行緒繼續執行i++操作,完成後i就為1了,接著切換到B執行緒,因為之前已經讀取過了,所以繼續執行i++操作,最後的結果i就為1了。同理可以解釋為什麼每次執行結果都是小於10000的數字。
但是使用synchronized對部分程式碼進行如下修改,就能保證同一時刻只有一個執行緒獲取鎖然後執行同步程式碼。執行結果必然是10000。

public  int inc = 0;
public synchronized void increase() {
        inc++;
}

本文整理參考自:
http://www.cnblogs.com/dolphin0520/p/3920373.html以及warmor的部落格。
--------------------- 
作者:SEU_Calvin 
來源:CSDN 
原文:https://blog.csdn.net/SEU_Calvin/article/details/52370068 
版權宣告:本文為博主原創文章,轉載請附上博文連結!

相關推薦

Java併發——執行同步VolatileSynchronized

0. 前言 轉載請註明出處:http://blog.csdn.net/seu_calvin/article/details/52370068 面試時很可能遇到這樣一個問題:使用volatile修飾int型變數i,多個執行緒同時進行i++操作,這樣可以實現執行緒安全嗎?提到

JAVA執行volatile synchronized 的比較

一,volatile關鍵字的可見性 要想理解volatile關鍵字,得先了解下JAVA的記憶體模型,Java記憶體模型的抽象示意圖如下: 從圖中可以看出: ①每個執行緒都有一個自己的本地記憶體空間--執行緒棧空間???執行緒執行時,先把變數從主記憶體讀取到執行緒自己

Java高階-執行同步lockunlock使用

一、Lock與Synchronized區別 Java中可以使用Lock和Synchronized的可以實現對某個共享資源的同步,同時也可以實現對某些過程的原子性操作。 Lock可以使用Condition進行執行緒之間的排程,Synchronized則使用Object物

Java執行池原理例項

Wiki 採用new Thread的方式產生多執行緒,可能有以下一些問題:  執行緒的建立和銷燬開銷很大,尤其是有些執行緒的存在時間較短;  執行緒的建立和銷燬過程中伴隨著CPU線上程間的切換,開銷很大; 執行緒池的優點有:  減少了

java執行管理 concurrent包用法

      我們都知道,在JDK1.5之前,Java中要進行業務併發時,通常需要有程式設計師獨立完成程式碼實現,當然也有一些開源的框架提供了這些功能,但是這些依然沒有JDK自帶的

Java執行】Executor框架的

在Java中,使用執行緒來非同步執行任務。Java執行緒的建立與銷燬需要一定的開銷,如果我們為每一個任務建立一個新執行緒來執行,這些執行緒的建立與銷燬將消耗大量的計算資源。同時,為每一個任務建立一個新執行緒來執行,這種策略可能會使處於高負荷狀態的應用最終崩潰。 Java執行

Java執行之fork/join框架

這個框架的目的主要是更好地利用底層平臺上的多核CPU和多處理器來進行處理,解決問題時通常使用分治演算法或map/reduce演算法來進行.這個框架的名稱來源於使用時的兩個基本操作fork和join,可以類比於map/reduce中的map和reduce操作.fork操作的作

java執行中的join方法

 方法Join是幹啥用的? 簡單回答,同步,如何同步? 怎麼實現的? 下面將逐個回答。     自從接觸Java多執行緒,一直對Join理解不了。JDK是這樣說的:join public final void join(long millis)throws InterruptedException Wait

Jmeter系列(11)- 併發執行組Concurrency Thread Group

如果你想從頭學習Jmeter,可以看看這個系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html   Concurrency Thread Group的介紹 Concurrency Thread Group提供了用於配置多個執行緒計劃

Java執行--同步死鎖:synchronized;等待喚醒:wait、notify、notifyAll;生命週期

class Info{ // 定義資訊類 private String name = "李興華"; // 定義name屬性 private String content = "JAVA講師" ; // 定義content屬性 private boolean flag = false ; // 設

Java執行同步 鎖機制synchronized

要是很多人在等這把鑰匙,等鑰匙還回來以後,誰會優先得到鑰匙?Not guaranteed。象前面例子裡那個想連續使用兩個上鎖房間的傢伙,他中間還鑰匙的時候如果還有其他人在等鑰匙,那麼沒有任何保證這傢伙能再次拿到。 (JAVA規範在很多地方都明確說明不保證,象Thread.sleep()休息後多久會返回執行,相

java執行程式設計之使用Synchronized同步變數

通過synchronized塊來同步特定的靜態或非靜態方法。 要想實現這種需求必須為這些特性的方法定義一個類變數,然後將這些方法的程式碼用synchronized塊括起來,並將這個類變數作為引數傳入synchronized塊   下面的程式碼演示瞭如何同步特定的類方法:

Java執行學習筆記(六) synchronized(this)同步語句塊

synchronized (this)同步語句塊 1. 一半非同步,一半同步 1.1 Task 1.2 ThreadA 1.3 ThreadB 1.4 Test 1.5 執行結果 2. synchronize

Java執行學習筆記(二) synchronized同步方法-防止髒讀

1. 髒讀 在給一個物件賦值的時候進行了同步, 但是在取值的時候可能出現意外,此值已經被其他執行緒修改了,這種情況就是髒讀 1.1 PublicVar類 public class PublicVar { public String userName = "wang don

Java執行學習筆記(一) synchronized同步方法

synchronized同步方法 1.提出問題-例項變數非執行緒安全 1.1 何為非執行緒安全? 1.2 舉例 1.2.1 有私有變數的類HasPrivateNum (供多執行緒們去呼叫) 1.2.2 執行緒A

java執行 同步 觀察者 併發集合的一個例子

//第一版 package com.hra.riskprice; import com.hra.riskprice.SysEnum.Factor_Type; import org.springframework.boot.SpringApplication; import org.springfram

java執行同步Synchronized,監視器monitor和鎖lock的關係是什

既然有關監視器monitor的概念比較難,大家怎麼解釋的都有。首先我給出一下java的官方文件,也是最權威的解釋: Synchronizationis built around an internal entity known as the intrinsic lock ormonitor lock. (Th

【轉】Java執行-同步集合和併發集合

同步集合可以簡單地理解為通過synchronized來實現同步的集合。如果有多個執行緒呼叫同步集合的方法,它們將會序列執行。 arrayList和vector、stack Vector是執行緒安全的,原始碼中有很多的synchronized可以看出,而

Java執行同步同步程式碼塊、wait、notifynotifyAll的真正含義工作原理

今天在和導師討論Java多執行緒程式設計的同步問題時,發現自己對同步程式碼塊、wait()方法、notify()方法和notifyAll()方法的理解不太清晰,於是在網上查閱資料,可是結果眾說紛紜,又在

Java執行同步 synchronized 用法

在使用MongoDB的時候 (基於spring-mongo) ,我想在插入物件時獲取有序自增的主鍵 ,但是MongoDB的預設規則是生成一串無序 (大致有序) 的字串 .而Spring Data提供的主鍵生成方法也是隨機的 String/BigInteger