成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java - 并發(fā) atomic, synchronization and volatile

StonePanda / 1400人閱讀

摘要:線程的這種交叉操作會導(dǎo)致線程不安全。原子操作是在多線程環(huán)境下避免數(shù)據(jù)不一致必須的手段。如果聲明一個域為一些情況就可以確保多線程訪問到的變量是最新的。并發(fā)要求一個線程對對象進行了操作,對象發(fā)生了變化,這種變化應(yīng)該對其他線程是可見的。

雖是讀書筆記,但是如轉(zhuǎn)載請注明出處 http://segmentfault.com/blog/exploring/
.. 拒絕伸手復(fù)制黨

一個問題:

i++為什么是非線程安全的?

先來解釋下什么叫“線程安全” :

  

Thread Safe describe some code that can be called from multiple threads without corrupting the state of the object or simply doing the thing the code must do in right order.

即一段代碼可以被多個線程調(diào)用,調(diào)用過程中對象的狀態(tài)不出現(xiàn)沖突,或者對象按照正確的順序進行了操作。

i++ 線程安全是指我們讀取一個值希望的是每一次讀取到的值都是上一次+1 。

i++是分為三個步驟,獲取i的值;temp = i+1操作;temp寫入i; 如果存在兩個線程,都執(zhí)行i++. 正常情況應(yīng)該是線程A 先執(zhí)行,得到1; 線程B再執(zhí)行,得到2.

但是又常常出現(xiàn):

線程A : 獲取i的值;得到0;temp = i+1操作;得到i= 1;

線程B : 獲取i的值;得到0;temp = i+1操作;得到i= 1;

線程A : i = temp 賦值 i =1 被寫入;

線程B :i = temp 賦值 i =1 被寫入;

或者更形象的舉例:線程A,B對i不停的進行操作,A執(zhí)行i++, B執(zhí)行打印。程序的邏輯是每次加1后就打,這樣應(yīng)該輸出的結(jié)果是順序的不斷加1。由于i++不是原子操作,在執(zhí)行的過程中發(fā)生了線程的切換,i+1沒有被回寫之前就被2訪問了,這時打印的還是原來的數(shù)字,并不是預(yù)期的+1。

線程的這種交叉操作會導(dǎo)致線程不安全。在Java中可以有很多方法來保證線程安全,即原子化 —— 同步,使用原子類,實現(xiàn)并發(fā)鎖,使用volatile關(guān)鍵字,使用不變類和線程安全類。

名詞解釋:何為 Atomic?

  

Atomic 一詞跟原子有點關(guān)系,后者曾被人認為是最小物質(zhì)的單位。計算機中的 Atomic 是指不能分割成若干部分的意思。如果一段代碼被認為是 Atomic, 原子操作是指一個不受其他操作影響的操作任務(wù)單元,原子操作不能中斷。原子操作是在多線程環(huán)境下避免數(shù)據(jù)不一致必須的手段。通常來說,原子指令由硬件提供,供軟件來實現(xiàn)原子方法(某個線程進入該方法后,就不會被中斷,直到其執(zhí)行完成)

如何實現(xiàn)原子操作

為了解決這個問題,必須保證增加操作是原子的,在 JDK1.5 之前我們可以使用同步技術(shù)(synchonized關(guān)鍵字, 鎖)來做到這一點。到 JDK1.5,java.util.concurrent.atomic 包提供了 int 和 long 類型的裝類,它們可以自動的保證對于他們的操作是原子的并且不需要使用同步。

同步技術(shù)/鎖 :synchronized 關(guān)鍵字修飾,給方法自動獲取和釋放鎖

public class Example {
    private int value = 0;    

    public synchronized int getNextValue(){
        return value++;
    }
}

或者

public class Example {
    private int value = 0;

    public int getNextValue() {
        synchronized (this) {
            return value++;
        }
    }
}

或者想對其他對象加鎖,而非當(dāng)前對象

public class Example {
    private int value = 0;

    private final Object lock = new Object();

    public int getNextValue() {
        synchronized (lock) {
            return value++;
        }
    }
}
Volatile

關(guān)鍵詞:可見性

當(dāng)對非volatile變量進行讀寫的時候,每個線程先從內(nèi)存拷貝變量到CPU緩存中。如果計算機有多個CPU,每個線程可能在不同的CPU上被處理,這意味著每個線程可以考慮到不同的CPU cache中。

而聲明變量是volatile的,JVM保證了每次讀變量都從內(nèi)存中讀,跳過CPU cache這一步。

編譯器可以改變指令執(zhí)行的順序以使吞吐量最大化,這種順序上的便會導(dǎo)致內(nèi)存的值不同步。

volatile關(guān)鍵字為實例域的同步訪問提供了一種免鎖機制。如果聲明一個域為volatile. 一些情況就可以確保多線程訪問到的變量是最新的。(并發(fā)要求)

javapublic class SharedObject{
    public volatile int counter = 0;
    }
  

The problem with multiple threads that do not see the latest value of a variable because that value has not yet been written back to main memory by another thread, is called a "visibility" problem. The updates of one thread are not visible to other threads.

一個線程對對象進行了操作,對象發(fā)生了變化,這種變化應(yīng)該對其他線程是可見的。但是默認對這點沒有任何保障。所以我們使用了Synchonized. 另一種方法是使用volatile關(guān)鍵字確保多線程對對象讀寫的可見性(但是只是在某些情況可以保證同步,比如一個線程讀,然后寫在了volatile變量上,其他線程只是進行讀操作; 如果多個線程都進行讀寫,那么就一定要在用synchronized)。volatile只確保了可見性,并不能確保原子性。

當(dāng)我們使用 volatile 關(guān)鍵字去修飾變量的時候,所以線程都會直接讀取該變量并且不緩存它。這就確保了線程讀取到的變量是同內(nèi)存中是一致的

原子操作類

幾乎 java.util.concurrent 包中的所有類都使用原子變量,而不使用同步。原因是 同步(lock)機制并不是一個輕量級的操作,它存在一些缺點。缺點如下

via Baptiste Vicht

 When several threads try to acquire the same lock, one or more threads will be suspended and they will be resumed later. When the critical section is little, the overhead is really heavy especially when the lock is often acquired and there is a lot of contention. Another disadvantage is that the other threads waiting of the lock cannot do something else during waiting and if the thread who has the lock is delayed (due to a page fault or the end of the time quanta by example), the others threads cannot take their turn.

JUC這包里面提供了一組原子類。其基本的特性就是在多線程環(huán)境下,當(dāng)有多個線程同時執(zhí)行這些類的實例包含的方法時,具有排他性,即當(dāng)某個線程進入方法,執(zhí)行其中的指令時,不會被其他線程打斷,而別的線程就像自旋鎖一樣,一直等到該方法執(zhí)行完成,才由 JVM 從等待隊列中選擇一個另一個線程進入,這只是一種邏輯上的理解。實際上是借助硬件的相關(guān)指令來實現(xiàn)的,不會阻塞線程 (或者說只是在硬件級別上阻塞了)。

根據(jù)修改的數(shù)據(jù)類型,可以將 JUC 包中的原子操作類可以分為 4 類。

基本類型: AtomicInteger, AtomicLong, AtomicBoolean ;

數(shù)組類型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray ;

引用類型: AtomicReference, AtomicStampedRerence, AtomicMarkableReference ;

對象的屬性修改類型: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater 。

這些類都是基于CAS實現(xiàn)的。處理器提供了CAS操作來實現(xiàn)非加鎖的原子操作。

引用《Java Concurrency in Practice》里的一段描述:

  

在這里,CAS 指的是現(xiàn)代 CPU 廣泛支持的一種對內(nèi)存中的共享數(shù)據(jù)進行操作的一種特殊指令。這個指令會對內(nèi)存中的共享數(shù)據(jù)做原子的讀寫操作。簡單介紹一下這個指令的操作過程:首先,CPU 會將內(nèi)存中將要被更改的數(shù)據(jù)與期望的值做比較。然后,當(dāng)這兩個值相等時,CPU 才會將內(nèi)存中的數(shù)值替換為新的值。否則便不做操作。最后,CPU 會將舊的數(shù)值返回。這一系列的操作是原子的。它們雖然看似復(fù)雜,但卻是 Java 5 并發(fā)機制優(yōu)于原有鎖機制的根本。簡單來說,CAS 的含義是 “我認為原有的值應(yīng)該是什么,如果是,則將原有的值更新為新值,否則不做修改,并告訴我原來的值是多少”。
CSA的優(yōu)點:Compare and Set 是一個非阻塞的算法,這是它的優(yōu)勢。因為使用的是 CPU 支持的指令,提供了比原有的并發(fā)機制更好的性能和伸縮性??梢哉J為一般情況下性能更好,并且也更容易使用

使用原子類實現(xiàn)i++方法

public class AtomicCounter {
    private final AtomicInteger value = new AtomicInteger(0);

    public int getValue(){
        return value.get();
    }

    public int getNextValue(){
        return value.incrementAndGet();
    }

    public int getPreviousValue(){
        return value.decrementAndGet();
    }
}

一個線程安全的棧

public class Stack {
    private final AtomicReference head = new AtomicReference(null);

    public void push(String value){
        Element newElement = new Element(value);

        while(true){
            Element oldHead = head.get();
            newElement.next = oldHead;

            //Trying to set the new element as the head
            if(head.compareAndSet(oldHead, newElement)){
                return;
            }
        }
    }

    public String pop(){
        while(true){
            Element oldHead = head.get();

            //The stack is empty
            if(oldHead == null){
                return null;
            }

            Element newHead = oldHead.next;

            //Trying to set the new element as the head
            if(head.compareAndSet(oldHead, newHead)){
                return oldHead.value;
            }
        }
    }

    private static final class Element {
        private final String value;
        private Element next;

        private Element(String value) {
            this.value = value;
        }
    }
}
總結(jié)

總結(jié)說來,synchronized 實現(xiàn)的同步能確保線程安全,實現(xiàn)可見性和原子性;但是代價大,效率低,更慢;
volatile 能夠?qū)崿F(xiàn)多線程操作產(chǎn)生變化的可見性,但是不能實現(xiàn)原子性。
atomic 類 是一種更輕量級的方法實現(xiàn)可見性和原子性

想更一進步的支持我,請掃描下方的二維碼,你懂的~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/64313.html

相關(guān)文章

  • BATJ都愛問的多線程面試題

    摘要:今天給大家總結(jié)一下,面試中出鏡率很高的幾個多線程面試題,希望對大家學(xué)習(xí)和面試都能有所幫助。指令重排在單線程環(huán)境下不會出先問題,但是在多線程環(huán)境下會導(dǎo)致一個線程獲得還沒有初始化的實例。使用可以禁止的指令重排,保證在多線程環(huán)境下也能正常運行。 下面最近發(fā)的一些并發(fā)編程的文章匯總,通過閱讀這些文章大家再看大廠面試中的并發(fā)編程問題就沒有那么頭疼了。今天給大家總結(jié)一下,面試中出鏡率很高的幾個多線...

    高勝山 評論0 收藏0
  • Java并發(fā)】線程安全性

    摘要:另一個是使用鎖的機制來處理線程之間的原子性。依賴于去實現(xiàn)鎖,因此在這個關(guān)鍵字作用對象的作用范圍內(nèi),都是同一時刻只能有一個線程對其進行操作的。 線程安全性 定義:當(dāng)多個線程訪問某個類時,不管運行時環(huán)境采用何種調(diào)度方式或者這些線程將如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個類都能表現(xiàn)出正確的行為,那么就稱這個類是線程安全的。 線程安全性主要體現(xiàn)在三個方面:原子性、可見性...

    劉玉平 評論0 收藏0
  • 并發(fā) - 基礎(chǔ)

    摘要:異步非阻塞方式,任務(wù)的完成的通知由其他線程發(fā)出。并發(fā)并行死鎖饑餓活鎖死鎖線程持有,線程持有。如等,在多線程情況下,該操作不是原子級別的而是原子的,所以一般用于狀態(tài)標(biāo)記。 同步/異步、阻塞/非阻塞 同步/異步是 API 被調(diào)用者的通知方式。阻塞/非阻塞則是 API 調(diào)用者的等待方式(線程掛機/不掛起)。 同步非阻塞 Future方式,任務(wù)的完成要主線程自己判斷。如NIO,后臺有多個任務(wù)在...

    phpmatt 評論0 收藏0
  • 貓頭鷹的深夜翻譯:核心JAVA并發(fā)(一)

    摘要:簡介從創(chuàng)建以來,就支持核心的并發(fā)概念如線程和鎖。這篇文章會幫助從事多線程編程的開發(fā)人員理解核心的并發(fā)概念以及如何使用它們。請求操作系統(tǒng)互斥,并讓操作系統(tǒng)調(diào)度程序處理線程停放和喚醒。 簡介 從創(chuàng)建以來,JAVA就支持核心的并發(fā)概念如線程和鎖。這篇文章會幫助從事多線程編程的JAVA開發(fā)人員理解核心的并發(fā)概念以及如何使用它們。 (博主將在其中加上自己的理解以及自己想出的例子作為補充) 概念 ...

    Richard_Gao 評論0 收藏0
  • java并發(fā)系列 - 第21天:java中的CAS操作,java并發(fā)的基石

    摘要:方法由兩個參數(shù),表示期望的值,表示要給設(shè)置的新值。操作包含三個操作數(shù)內(nèi)存位置預(yù)期原值和新值。如果處的值尚未同時更改,則操作成功。中就使用了這樣的操作。上面操作還有一點是將事務(wù)范圍縮小了,也提升了系統(tǒng)并發(fā)處理的性能。 這是java高并發(fā)系列第21篇文章。 本文主要內(nèi)容 從網(wǎng)站計數(shù)器實現(xiàn)中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及數(shù)據(jù)庫...

    zorro 評論0 收藏0

發(fā)表評論

0條評論

StonePanda

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<