• <nav id="wkkge"><strong id="wkkge"></strong></nav>
  • <menu id="wkkge"></menu>
  • Java面向對象
    Java異常
    Java數組
    Java常用類
    Java集合
    Java IO流
    Java線程
    Java反射
    Socket編程
    Java注解開發
    Java GoF設計模式
    HashMap
    Java內存模型
    Java線性表

    Java并發編程

     

     

    原子性

     

    原子性:即一個操作或者多個操作 要么全部執行并且執行的過程不會被任何因素打斷,要么就都不執行。

     

    一個很經典的例子就是銀行賬戶轉賬問題:

     

    比如從賬戶A向賬戶B轉1000元,那么必然包括2個操作:從賬戶A減去1000元,往賬戶B加上1000元。

     

    試想一下,如果這2個操作不具備原子性,會造成什么樣的后果。假如從賬戶A減去1000元之后,操作突然中止。然后又從B取出了500元,取出500元之后,再執行 往賬戶B加上1000元 的操作。這樣就會導致賬戶A雖然減去了1000元,但是賬戶B沒有收到這個轉過來的1000元。

     

    所以這2個操作必須要具備原子性才能保證不出現一些意外的問題。

     

    同樣地反映到并發編程中會出現什么結果呢?

     

    舉個最簡單的例子,大家想一下假如為一個32位的變量賦值過程不具備原子性的話,會發生什么后果?

     

    i = 9;

     

    假若一個線程執行到這個語句時,我暫且假設為一個32位的變量賦值包括兩個過程:為低16位賦值,為高16位賦值。

     

    那么就可能發生一種情況:當將低16位數值寫入之后,突然被中斷,而此時又有一個線程去讀取i的值,那么讀取到的就是錯誤的數據。

     

     

    可見性

     

    可見性是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。

     

    舉個簡單的例子,看下面這段代碼:

    
     
    //線程1執行的代碼
    int i = 0;
    i = 10;
    //線程2執行的代碼
    j = i;

     

    假若執行線程1的是CPU1,執行線程2的是CPU2。由上面的分析可知,當線程1執行 i =10這句時,會先把i的初始值加載到CPU1的高速緩存中,然后賦值為10,那么在CPU1的高速緩存當中i的值變為10了,卻沒有立即寫入到主存當中。

     

    此時線程2執行 j = i,它會先去主存讀取i的值并加載到CPU2的緩存當中,注意此時內存當中i的值還是0,那么就會使得j的值為0,而不是10。

     

    這就是可見性問題,線程1對變量i修改了之后,線程2沒有立即看到線程1修改的值。

     

     

    有序性

     

    有序性:即程序執行的順序按照代碼的先后順序執行。舉個簡單的例子,看下面這段代碼:

     

    int i = 0;              
    boolean flag = false;
    i = 1;                //語句1  
    flag = true;          //語句2

     

    上面代碼定義了一個int型變量,定義了一個boolean類型變量,然后分別對兩個變量進行賦值操作。從代碼順序上看,語句1是在語句2前面的,那么JVM在真正執行這段代碼的時候會保證語句1一定會在語句2前面執行嗎?不一定,為什么呢?這里可能會發生指令重排序(Instruction Reorder)。

     

    下面解釋一下什么是指令重排序,一般來說,處理器為了提高程序運行效率,可能會對輸入代碼進行優化,它不保證程序中各個語句的執行先后順序同代碼中的順序一致,但是它會保證程序最終執行結果和代碼順序執行的結果是一致的。

     

    比如上面的代碼中,語句1和語句2誰先執行對最終的程序結果并沒有影響,那么就有可能在執行過程中,語句2先執行而語句1后執行。

     

    但是要注意,雖然處理器會對指令進行重排序,但是它會保證程序最終結果會和代碼順序執行結果相同,那么它靠什么保證的呢?再看下面一個例子:

     

    上面代碼定義了一個int型變量,定義了一個boolean類型變量,然后分別對兩個變量進行賦值操作。從代碼順序上看,語句1是在語句2前面的,那么JVM在真正執行這段代碼的時候會保證語句1一定會在語句2前面執行嗎?不一定,為什么呢?這里可能會發生指令重排序(Instruction Reorder)。

     

    下面解釋一下什么是指令重排序,一般來說,處理器為了提高程序運行效率,可能會對輸入代碼進行優化,它不保證程序中各個語句的執行先后順序同代碼中的順序一致,但是它會保證程序最終執行結果和代碼順序執行的結果是一致的。

     

    比如上面的代碼中,語句1和語句2誰先執行對最終的程序結果并沒有影響,那么就有可能在執行過程中,語句2先執行而語句1后執行。

     

    但是要注意,雖然處理器會對指令進行重排序,但是它會保證程序最終結果會和代碼順序執行結果相同,那么它靠什么保證的呢?再看下面一個例子:

     

    int a = 10;    //語句1
    int r = 2;    //語句2
    a = a + 3;    //語句3
    r = a*a;     //語句4
    
    

    這段代碼有4個語句,那么可能的一個執行順序是:這段代碼有4個語句,那么可能的一個執行順序是:

     

     

    那么可不可能是這個執行順序呢: 語句2 語句1 語句4 語句3

     

    不可能,因為處理器在進行重排序時是會考慮指令之間的數據依賴性,如果一個指令Instruction 2必須用到Instruction 1的結果,那么處理器會保證Instruction 1會在Instruction 2之前執行。

     

    雖然重排序不會影響單個線程內程序執行的結果,但是多線程呢?下面看一個例子:

     

    //線程1:
    context = loadContext();   //語句1
    inited = true;             //語句2
    //線程2:
    while(!inited ){
      			sleep()
    }
    doSomethingwithconfig(context);

     

    上面代碼中,由于語句1和語句2沒有數據依賴性,因此可能會被重排序。假如發生了重排序,在線程1執行過程中先執行語句2,而此是線程2會以為初始化工作已經完成,那么就會跳出while循環,去執行doSomethingwithconfig(context)方法,而此時context并沒有被初始化,就會導致程序出錯。

     

    從上面可以看出,指令重排序不會影響單個線程的執行,但是會影響到線程并發執行的正確性。

     

    也就是說,要想并發程序正確地執行,必須要保證原子性、可見性以及有序性。只要有一個沒有被保證,就有可能會導致程序運行不正確。

     

     

    全部教程
  • <nav id="wkkge"><strong id="wkkge"></strong></nav>
  • <menu id="wkkge"></menu>
  • 面对面棋牌游戏