Java 中的volitle 關鍵字

來源:互聯網
上載者:User

標籤:

http://blog.csdn.net/liujinwei2005/article/details/6295666

原帖:http://rwl6813021.javaeye.com/blog/349169

 

 

研究ThreadPoolExecutor的時候,發現其中大量使用了volatile變數。不知為何,因此做了一番尋找,研究: 其中借鑒了很多網上資料。 在瞭解volatile變數作用前,先需要明白一些概念: 

什麼是原子操作? 
所謂原子操作,就是"不可中斷的一個或一系列操作" , 在確認一個操作是原子的情況下,多線程環境裡面,我們可以避免僅僅為保護這個操作在外圍加上效能昂貴的鎖,甚至藉助於原子操作,我們可以實現互斥鎖。 很多作業系統都為int類型提供了+-賦值的原子操作版本,比如 NT 提供了 InterlockedExchange 等API, Linux/UNIX也提供了atomic_set 等函數。 

關於java中的原子性? 
原子性可以應用於除long和double之外的所有基本類型之上的“簡單操作”。對於讀取和寫入出long double之外的基本類型變數這樣的操作,可以保證它們會被當作不可分(原子)的操作來操作。 因為JVM的版本和其它的問題,其它的很多操作就不好說了,比如說++操作在C++中是原子操作,但在Java中就不好說了。 另外,Java提供了AtomicInteger等原子類。再就是用原子性來控制並發比較麻煩,也容易出問題。 

volatile原理是什麼? 
Java中volatile關鍵字原義是“不穩定、變化”的意思 
使用volatile和不使用volatile的區別在於JVM記憶體主存和線程工作記憶體的同步之上。volatile保證變數線上程工作記憶體和主存之間一致。 
其實是告訴處理器, 不要將我放入工作記憶體, 請直接在主存操作我. 

接下來是測試 :(通過測試能更好的發現和分析問題) 
申明了幾種整形的變數,開啟100個線程同時對這些變數進行++操作,發現結果差異很大: 
>>Execute End: 
>>Atomic: 100000 
>>VInteger: 38790 
>>Integer: 68749 
>>Source i: 99205 
>>Source Vi: 99286 
也就是說除了Atomic,其他的都是錯誤的。 

我們通過一些疑問,來解釋一下。 

1:為什麼會產生錯誤的資料? 
多線程引起的,因為對於多線程同時操作一個整型變數在大並行作業的情況下無法做到同步,而Atom提供了很多針對此類安全執行緒問題的解決方案,因此解決了同時讀寫操作的問題。


2:為什麼會造成同步問題? 
Java多線程在對變數進行操作的時候,實際上是每個線程會單獨分配一個針對i值的拷貝(獨立記憶體地區),但是申明的i值確是在主記憶體地區中,當對i值修改完畢後,線程會將自己記憶體地區塊中的i值拷貝到主記憶體地區中,因此有可能每個線程拿到的i值是不一樣的,從而出現了同步問題。


3:為什麼使用volatile修飾integer變數後,還是不行? 
因為volatile僅僅只是解決了儲存的問題,即i值只是保留在了一個記憶體地區中,但是i++這個操作,涉及到擷取i值、修改i值、儲存i值(i=i+1),這裡的volatile只是解決了儲存i值得問題,至於擷取和修改i值,確是沒有做到同步。


4:既然不能做到同步,那為什麼還要用volatile這種修飾符? 
主要的一個原因是方便,因為只需添加一個修飾符即可,而無需做對象加鎖、解鎖這麼麻煩的操作。但是本人不推薦使用這種機制,因為比較容易出問題(髒資料),而且也保證不了同步。


5:那到底如何解決這樣的問題? 
        第一種:採用同步synchronized解決,這樣雖然解決了問題,但是也降低了系統的效能。 
        第二種:採用原子性資料Atomic變數,這是從JDK1.5開始才存在的針對原子性的解決方案,這種方案也是目前比較好的解決方案了。


6:Atomic的實現基本原理? 
首先Atomic中的變數是申明為了volatile變數的,這樣就保證的變數的儲存和讀取是一致的,都是來自同一個記憶體塊,然後Atomic提供了getAndIncrement方法,該方法對變數的++操作進行了封裝,並提供了compareAndSet方法,來完成對單個變數的加鎖和解鎖操作,方法中用到了一個UnSafe的對象,現在還不知道這個UnSafe的工作原理(似乎沒有公開原始碼)。Atomic雖然解決了同步的問題,但是效能上面還是會有所損失,不過影響不大,網上有針對這方面的測試,大概50million的操作對比是250ms : 850ms,對於大部分的高效能應用,應該還是夠的了。

package qflag.ucstar.test.thread;   

  

import java.util.concurrent.atomic.AtomicInteger;    
   
/** 
* 測試原子性的同步 
* @author polarbear 2009-3-14 

*/   
public class TestAtomic {    
        
    public static AtomicInteger astom_i = new AtomicInteger();    
        
    public static volatile Integer v_integer_i = 0;    
        
    public static volatile int v_i = 0;    
        
    public static Integer integer_i = 0;    
        
    public static int i = 0;    
        
    public static int endThread = 0;    
        
    public static void main(String[] args) {    
        new TestAtomic().testAtomic();    
     }    
        
    public void testAtomic() {    
            
        for(int i=0; i<100; i++) {    
            new Thread(new IntegerTestThread()).start();    
         }    
            
        try {    
            for(;;) {    
                 Thread.sleep(500);    
                if(TestAtomic.endThread == 100) {    
                     System.out.println(">>Execute End:");    
                     System.out.println(">>Atomic: /t"+TestAtomic.astom_i);    
                     System.out.println(">>VInteger: /t"+TestAtomic.v_integer_i);    
                     System.out.println(">>Integer: /t"+TestAtomic.integer_i);    
                     System.out.println(">>Source i: /t"+TestAtomic.i);    
                     System.out.println(">>Source Vi: /t"+TestAtomic.v_i);    
                    break;    
                 }    
             }    
                
         } catch (Exception e) {    
             e.printStackTrace();    
         }    
     }    
        
}    
class IntegerTestThread implements Runnable {    
    public void run() {    
        int x = 0;    
        while(x<1000) {    
             TestAtomic.astom_i.incrementAndGet();    
             TestAtomic.v_integer_i++;    
             TestAtomic.integer_i++;    
             TestAtomic.i++;    
             TestAtomic.v_i++;    
             x++;    
         }    
         ++TestAtomic.endThread;    //貌似很無敵!難道是原子性的嗎? 
     }    
}

-----------------------------------------xx-----------------------------------xx-----------------------------------------------

 

 

本人繼續補充:

除了TestAtomic.endThread,其他的變數都被忽略了。具體解釋可參見注釋。



import java.util.concurrent.atomic.AtomicInteger; 
import java.io.*;

/** 
* 測試原子性的同步 
* @author pyc 2009-3-29 

*/ 
public class TestAtomic {    
    public static final int N=10;
    public static final int M=10000;
    public static int perfect_result=M*N;
    public static int endThread = 0; 
    
private PrintWriter out;//將資訊輸入至文本"out.txt",因為控制台buffer可能不夠.

public TestAtomic() throws IOException
{
   out =new PrintWriter(
     new BufferedWriter(
       new FileWriter("out.txt")));

       
    public static void main(String[] args) { 
         try{ 
         new TestAtomic().testAtomic(); 
         }catch(Exception e){
         System.out.println(e.getMessage());
          }
         System.out.println("OK./nStatistical report:");
         System.out.println("Covered by "+(perfect_result-endThread)+" times.");
    }   
       
    public void testAtomic() { 
        Thread[] td=new Thread[N];
        for(int i=0; i<N; i++) {   
        td[i]=new Thread(new IntegerTestThread(i+1));
        }   
        for(int i=0; i<N; i++) {   
        td[i].start();
        out.println((i+1)+" go..") ; //此處如果run()方法代碼少,立即可觀察到complete完成資訊。
        }     
        try { 
        long temp=0; //存放了上次的endTread值。
        int count=1000; //如果temp值超過一千次的重複就可以認為結束程式。
        for(;;) { 
           //Thread.sleep(1); //有可能main線程運行過快,可以調節採樣的頻率。
           if(TestAtomic.endThread == perfect_result) {   
            out.println("==============/r/nPerfect!/r/n=============="); //完美匹配!
                break; 
            }
            if(temp==TestAtomic.endThread){
               out.println("Equal!!");//有重複,有可能是所有線程運行結束時的重複,也有可能是main線程採樣過快。
               count--;//倒計時中。。。
            }
            else {
               temp=TestAtomic.endThread;//給temp賦新值。
               count=1000;//重新設定倒計時。
            }
            out.println("endThread = "+TestAtomic.endThread);//在此處有幾率可觀察當前的endThread值比上次要少。
            //這是關鍵之處!
            if(count<=0)
            {
               out.println("/r/nI‘ll be crazy if I wait for that once again!/r/nFailed, OMG!+_+");
               break;
            }
        }   
        out.close();     
        }catch(Exception e) {   
            e.printStackTrace();   
        }   
    }   
    
    class IntegerTestThread implements Runnable { 
    private int id;
    public IntegerTestThread(int i){
       this.id=i;
    }
    public void run() {   
       int i=M;//充分保證線程重疊運行
       while(i>0){
        try{
         //Thread.sleep((int)(10*Math.random()));//設定睡眠時間,從而儘可能使線程重疊運行。
        }catch(Exception e){
        ++TestAtomic.endThread;//測試該語句的“原子”性。其實做完實驗,我們知道,
++i,i++,   i=i+1一樣都不能保證原子性。
        //我們可以從最終的endThread值是不是等於M*N得知。
        i--;
       }   
       out.println("************/r/n"+id+" has Completed!/r/n************/r/n") ;
    }   
    } 
}

Java 中的volitle 關鍵字

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.