欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Java中的線程安全性介紹-創(chuàng)新互聯(lián)

線程安全性概念

創(chuàng)新互聯(lián)公司于2013年開始,先為嵐皋等服務(wù)建站,嵐皋等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為嵐皋企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

當(dāng)對一個(gè)復(fù)雜對象進(jìn)行某種操作時(shí),從操作開始到操作結(jié)束,被操作的對象往往會(huì)經(jīng)歷若干非法的中間狀態(tài)。調(diào)用一個(gè)函數(shù)(假設(shè)該函數(shù)是正確的)操作某對象常常會(huì)使該對象暫時(shí)陷入不可用的狀態(tài)(通常稱為不穩(wěn)定狀態(tài)),等到操作完全結(jié)束,該對象才會(huì)重新回到完全可用的狀態(tài)。如果其他線程企圖訪問一個(gè)處于不可用狀態(tài)的對象,該對象將不能正確響應(yīng)從而產(chǎn)生無法預(yù)料的結(jié)果,如何避免這種情況發(fā)生是線程安全性的核心問題。

原子概念

當(dāng)方法調(diào)用似乎立即生效時(shí),該方法就是原子的。 因此,其他線程在方法調(diào)用之前或之后只能看到狀態(tài),而沒有中間狀態(tài)。 讓我們看一下非原子方法,看看原子方法如何使類具有線程安全性。

public class UniqueIdNotAtomic {
   private volatile long counter = 0;
   public  long nextId() { 
     return counter++;  
   }  
}

類UniqueIdNotAtomic通過使用易失性變量計(jì)數(shù)器創(chuàng)建唯一的ID。 我在第2行使用了volatile字段,以確保線程始終看到當(dāng)前值,如此處更詳細(xì)的說明。 要查看此類是否是線程安全的,我們使用以下測試:

public class TestUniqueIdNotAtomic {
   private final UniqueIdNotAtomic uniqueId = new UniqueIdNotAtomic();
   private long firstId;
   private long secondId;
   private void updateFirstId() {
     firstId  = uniqueId.nextId();
   }
   private void updateSecondId() {
     secondId = uniqueId.nextId();
   }
   @Test
   public void testUniqueId() throws InterruptedException {   
     try (AllInterleavings allInterleavings = 
         new AllInterleavings("TestUniqueIdNotAtomic");) {
     while(allInterleavings.hasNext()) { 
     Thread first = new Thread( () ->  { updateFirstId();  } ) ;
     Thread second = new Thread( () ->  { updateSecondId();  } ) ;
     first.start();
     second.start();
     first.join();
     second.join();  
     assertTrue(  firstId != secondId );
     }
     }
   }
}

為了測試計(jì)數(shù)器是否是線程安全的,我們需要在第16和17行中創(chuàng)建兩個(gè)線程。我們啟動(dòng)這兩個(gè)線程(第18和19行)。然后,我們等待直到兩個(gè)線程都通過第20和21行結(jié)束。 在兩個(gè)線程都停止之后,我們檢查兩個(gè)ID是否唯一,如第22行所示。

為了測試所有線程交織,我們使用來自vmlens第15行的AllInterleavings類,將完整的測試放在while循環(huán)中迭代所有線程交織。

運(yùn)行測試,我們看到以下錯(cuò)誤:

java.lang.AssertionError: 
   at org.junit.Assert.fail(Assert.java:91)
   at org.junit.Assert.assertTrue(Assert.java:43)

發(fā)生該錯(cuò)誤的原因是,由于操作++不是原子操作,因此兩個(gè)線程可以覆蓋另一個(gè)線程的結(jié)果。 我們可以在vmlens的報(bào)告中看到這一點(diǎn):

在發(fā)生錯(cuò)誤的情況下,兩個(gè)線程首先并行讀取變量計(jì)數(shù)器。 然后,兩個(gè)都創(chuàng)建相同的ID。 為了解決這個(gè)問題,我們通過使用同步塊使方法原子化:

private final Object LOCK = new Object();
public  long nextId() {
  synchronized(LOCK) {
   return counter++;  
  } 
}

現(xiàn)在,該方法是原子的。 同步塊可確保其他線程無法看到該方法的中間狀態(tài)。

不訪問共享狀態(tài)的方法是自動(dòng)原子的。 具有只讀狀態(tài)的類也是如此。 因此,無狀態(tài)和不可變的類是實(shí)現(xiàn)線程安全類的簡便方法。 他們所有的方法都是自動(dòng)的。

并非原子方法的所有用法都是自動(dòng)線程安全的。 將多個(gè)原子方法組合為相同的值通常會(huì)導(dǎo)致爭用條件。 讓我們看看從ConcurrentHashMap獲取和放置的原子方法以了解原因。 當(dāng)以前的映射不存在時(shí),讓我們使用這些方法在映射中插入一個(gè)值:

public class TestUpdateTwoAtomicMethods {
   public void update(ConcurrentHashMap<Integer,Integer>  map)  {
       Integer result = map.get(1);     
       if( result == null )  {
         map.put(1, 1);
       }
       else   {
         map.put(1, result + 1 );
       }  
   }
   @Test
   public void testUpdate() throws InterruptedException   {
     try (AllInterleavings allInterleavings = 
      new AllInterleavings("TestUpdateTwoAtomicMethods");) {
     while(allInterleavings.hasNext()) { 
     final ConcurrentHashMap<Integer,Integer>  map = 
      new  ConcurrentHashMap<Integer,Integer>(); 
     Thread first = new Thread( () ->  { update(map);  } ) ;
     Thread second = new Thread( () ->  { update(map);  } ) ;
     first.start();
     second.start();
     first.join();
     second.join();  
     assertEquals( 2 , map.get(1).intValue() );
     }
     }
   }  
}

該測試與先前的測試相似。 再次,我們使用兩個(gè)線程來測試我們的方法是否是線程安全的(第18行和第19行)。再次,我們在兩個(gè)線程完成之后測試結(jié)果是否正確(第24行)。運(yùn)行測試,我們看到以下錯(cuò)誤:

java.lang.AssertionError: expected:<2> but was:<1>
   at org.junit.Assert.fail(Assert.java:91)
   at org.junit.Assert.failNotEquals(Assert.java:645)

該錯(cuò)誤的原因是,兩種原子方法get和put的組合不是原子的。 因此,兩個(gè)線程可以覆蓋另一個(gè)線程的結(jié)果。 我們可以在vmlens的報(bào)告中看到這一點(diǎn):

在發(fā)生錯(cuò)誤的情況下,兩個(gè)線程首先并行獲取值。 然后,兩個(gè)都創(chuàng)建相同的值并將其放入地圖中。 要解決這種競爭狀況,我們需要使用一種方法而不是兩種方法。 在我們的例子中,我們可以使用單個(gè)方法而不是兩個(gè)方法get和put來進(jìn)行計(jì)算:

public void update() {
  map.compute(1, (key, value) -> {
   if (value == null) {
     return 1;
   } 
   return value + 1;
  });
}

因?yàn)榉椒ㄓ?jì)算是原子的,所以這解決了競爭條件。 雖然對ConcurrentHashMap的相同元素進(jìn)行的所有操作都是原子操作,但對整個(gè)地圖(如大小)進(jìn)行操作的操作都是靜態(tài)的。 因此,讓我們看看靜態(tài)意味著什么。

“靜止”是什么意思?

靜態(tài)意味著當(dāng)我們調(diào)用靜態(tài)方法時(shí),我們需要確保當(dāng)前沒有其他方法在運(yùn)行。 下面的示例顯示如何使用ConcurrentHashMap的靜態(tài)方法大?。?/p>

ConcurrentHashMap<Integer,Integer>  map = 
   new  ConcurrentHashMap<Integer,Integer>();
Thread first  = new Thread(() -> { map.put(1,1);});
Thread second = new Thread(() -> { map.put(2,2);});
first.start();
second.start();
first.join();
second.join();  
assertEquals( 2 ,  map.size());

通過等待直到所有線程都使用線程連接完成為止,當(dāng)我們調(diào)用方法大小時(shí),我們確保沒有其他線程正在訪問ConcurrentHashMap。

方法大小使用在java.util.concurrent.atomic.LongAdder,LongAccumulator,DoubleAdder和DoubleAccumulator類中也使用的一種機(jī)制來避免爭用。 與其使用單個(gè)變量存儲當(dāng)前大小,不如使用數(shù)組。 不同的線程更新數(shù)組的不同部分,從而避免爭用。 該算法在Striped64的Java文檔中有更詳細(xì)的說明。

靜態(tài)類和靜態(tài)方法對于收集競爭激烈的統(tǒng)計(jì)數(shù)據(jù)很有用。 收集數(shù)據(jù)后,可以使用一個(gè)線程來評估收集的統(tǒng)計(jì)信息。

為什么在Java中沒有其他線程安全方法?

在理論計(jì)算機(jī)科學(xué)中,線程安全性意味著數(shù)據(jù)結(jié)構(gòu)滿足正確性標(biāo)準(zhǔn)。 最常用的正確性標(biāo)準(zhǔn)是可線性化的,這意味著數(shù)據(jù)結(jié)構(gòu)的方法是原子的。

對于常見的數(shù)據(jù)結(jié)構(gòu),存在可證明的線性化并發(fā)數(shù)據(jù)結(jié)構(gòu),請參見Maurice Herlihy和Nir Shavit撰寫的《多處理器編程的藝術(shù)》一書。 但是要使數(shù)據(jù)結(jié)構(gòu)線性化,需要使用比較和交換之類的昂貴同步機(jī)制,請參閱論文《定律:無法消除并發(fā)算法中的昂貴同步》以了解更多信息。

因此,研究了其他正確性標(biāo)準(zhǔn)(例如靜態(tài))。 因此,我認(rèn)為問題不在于“為什么Java中沒有其他類型的線程安全方法?” 但是,Java何時(shí)將提供其他類型的線程安全性?
結(jié)論

Java中的線程安全性意味著類的方法是原子的或靜態(tài)的。 當(dāng)方法調(diào)用似乎立即生效時(shí),該方法就是原子的。 靜態(tài)意味著當(dāng)我們調(diào)用靜態(tài)方法時(shí),我們需要確保當(dāng)前沒有其他方法在運(yùn)行。

目前,靜態(tài)方法僅用于收集統(tǒng)計(jì)信息,例如ConcurrentHashMap的大小。 對于所有其他用例,使用原子方法。 讓我們拭目以待,未來是否會(huì)帶來更多類型的線程安全方法。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

文章名稱:Java中的線程安全性介紹-創(chuàng)新互聯(lián)
標(biāo)題鏈接:http://chinadenli.net/article26/hidjg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、標(biāo)簽優(yōu)化、手機(jī)網(wǎng)站建設(shè)品牌網(wǎng)站制作、響應(yīng)式網(wǎng)站、微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁設(shè)計(jì)公司