国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Volatile與多線程

lijinke666 / 403人閱讀

摘要:一前言我們知道在多線程的場景下,線程安全是必須要著重考慮的。變量可用于提供線程安全,但是只能應(yīng)用于非常有限的一組用例多個(gè)變量之間或者某個(gè)變量的當(dāng)前值與修改后值之間沒有約束。

一、前言

我們知道在多線程的場景下,線程安全是必須要著重考慮的。Java語言包含兩種內(nèi)在的同步機(jī)制:同步塊(synchronize關(guān)鍵字)和 volatile 變量。但是其中 Volatile 變量雖然使用簡單,有時(shí)候開銷也比較低,但是同時(shí)它的同步性較差,而且其使用也更容易出錯(cuò)。下面我們先使用一個(gè)例子來展示下volatile有可能出現(xiàn)線程不安全的情況:

public class ShareDataVolatile {
    //同時(shí)創(chuàng)建十個(gè)線程,每個(gè)線程自增100次
    //主程序等待3秒讓所有線程全部運(yùn)行完畢后輸出最后的count值

    //使用volatile修飾計(jì)數(shù)變量count
    public volatile static int count=0;
    public static void main(String[] args){
        final ShareDataVolatile data  = new ShareDataVolatile();
        for(int i=0;i<10;i++){
            new Thread(
                    new Runnable(){
                        public void run(){
                            try{
                                Thread.sleep(1);
                            }catch(InterruptedException e){
                                e.printStackTrace();
                            }
                            for(int j=0;j<100;j++){
                                data.addCount();
                            }
                            System.out.print(count+" ");
                        }
                    }
                ).start();              
        }
        try{
            Thread.sleep(3000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println();
        System.out.print("count="+count);
    }
    public void addCount(){
        count++;
    }
}

運(yùn)行結(jié)果:

200 200 416 585 755 742 513 513 501 855 

count=855
多次運(yùn)行結(jié)果最后的count都不是預(yù)計(jì)的1000,這說明使用volatile變量并不能保證線程安全。

二、原因分析

鎖提供了兩種主要特性:互斥(mutual exclusion)和可見性(visibility)
互斥即一次只允許一個(gè)線程持有某個(gè)特定的鎖,因此可使用該特性實(shí)現(xiàn)對共享數(shù)據(jù)的協(xié)調(diào)訪問協(xié)議,這樣,一次就只有一個(gè)線程能夠使用該共享數(shù)據(jù)。可見性要更加復(fù)雜一些,它必須確保釋放鎖之前對共享數(shù)據(jù)做出的更改對于隨后獲得該鎖的另一個(gè)線程是可見的 —— 如果沒有同步機(jī)制提供的這種可見性保證,線程看到的共享變量可能是修改前的值或不一致的值,這將引發(fā)許多嚴(yán)重問題。
Volatile 變量具有 synchronized 的可見性特性,但是不具備原子特性。這就是說線程能夠自動發(fā)現(xiàn) volatile 變量的最新值。Volatile 變量可用于提供線程安全,但是只能應(yīng)用于非常有限的一組用例:多個(gè)變量之間或者某個(gè)變量的當(dāng)前值與修改后值之間沒有約束。因此,多帶帶使用 volatile 還不足以實(shí)現(xiàn)計(jì)數(shù)器、互斥鎖或任何具有與多個(gè)變量相關(guān)的不變式(Invariants)的類(例如 “start <=end”)。
所以例子中雖然增量操作(count++)看上去類似一個(gè)多帶帶操作,實(shí)際上它是一個(gè)由讀取-修改-寫入操作序列組成的組合操作,必須以原子方式執(zhí)行,而 volatile 不能對組合操作提供必須的原子特性。實(shí)現(xiàn)正確的操作需要使 count 的值在操作期間保持不變,而 volatile 變量無法實(shí)現(xiàn)這點(diǎn)。

三、JVM內(nèi)存操作模型

在 java 垃圾回收整理一文中,描述了jvm運(yùn)行時(shí)刻內(nèi)存的分配。其中有一個(gè)內(nèi)存區(qū)域是jvm虛擬機(jī)棧,每一個(gè)線程運(yùn)行時(shí)都有一個(gè)線程棧,線程棧保存了線程運(yùn)行時(shí)候變量值信息。當(dāng)線程訪問某一個(gè)對象時(shí)候值的時(shí)候,首先通過對象的引用找到對應(yīng)在堆內(nèi)存的變量的值,然后把堆內(nèi)存變量的具體值load到線程本地內(nèi)存中,建立一個(gè)變量副本,之后線程就不再和對象在堆內(nèi)存變量值有任何關(guān)系,而是直接修改副本變量的值,在修改完之后的某一個(gè)時(shí)刻(線程退出之前),自動把線程變量副本的值回寫到對象在堆中變量。這樣在堆中的對象的值就產(chǎn)生變化了。下面一幅圖描述這寫交互:

read and load 從主存復(fù)制變量到當(dāng)前工作內(nèi)存
use and assign 執(zhí)行代碼,改變共享變量值
store and write 用工作內(nèi)存數(shù)據(jù)刷新主存相關(guān)內(nèi)容
其中use and assign 可以多次出現(xiàn),但是這一些操作并不是原子性,也就是 在read load之后,如果主內(nèi)存count變量發(fā)生修改之后,線程工作內(nèi)存中的值由于已經(jīng)加載,不會產(chǎn)生對應(yīng)的變化,所以計(jì)算出來的結(jié)果會和預(yù)期不一樣對于volatile修飾的變量,jvm虛擬機(jī)只是保證從主內(nèi)存加載到線程工作內(nèi)存的值是最新的,例如:

假如線程1,線程2 在進(jìn)行ead,load 操作中,發(fā)現(xiàn)主內(nèi)存中count的值都是5,那么都會加載這個(gè)最新的值 
在線程1堆count進(jìn)行修改之后,會write到主內(nèi)存中,主內(nèi)存中的count變量就會變?yōu)?
線程2由于已經(jīng)進(jìn)行read,load操作,在進(jìn)行運(yùn)算之后,也會更新主內(nèi)存count的變量值為6
導(dǎo)致兩個(gè)線程及時(shí)用volatile關(guān)鍵字修改之后,還是會存在并發(fā)的情況。
四、Volatile的優(yōu)勢與使用條件

看了上面的,大家可能已經(jīng)對volatile表示十分失望,不打算使用它了,然后volatile的存在肯定有它存在的意義:

1.簡易性:在某些情形下,使用 volatile 變量要比使用相應(yīng)的鎖簡單得多。 
2.性能:某些情況下,volatile 變量同步機(jī)制的性能要優(yōu)于鎖。
對 JVM 內(nèi)在的操作而言,我們難以抽象地比較 volatile 和 synchronized 的開銷。但是大部分情況下,在目前大多數(shù)的處理器架構(gòu)上,volatile 讀操作開銷非常低 —— 幾乎和非 volatile 讀操作一樣。而 volatile 寫操作的開銷要比非 volatile 寫操作多很多,因?yàn)橐WC可見性需要實(shí)現(xiàn)內(nèi)存界定(Memory Fence),即便如此,volatile 的總開銷仍然要比鎖獲取低。
volatile 操作不會像鎖一樣造成阻塞,因此,在能夠安全使用 volatile 的情況下,volatile 可以提供一些優(yōu)于鎖的可伸縮特性。如果讀操作的次數(shù)要遠(yuǎn)遠(yuǎn)超過寫操作,與鎖相比,volatile 變量通常能夠減少同步的性能開銷。

所以我們需要明確可以使用volatile的條件有兩點(diǎn):

1.對變量的寫操作不依賴于當(dāng)前值。 
2.該變量沒有包含在具有其他變量的不變式中。
五、結(jié)束語

與鎖相比,Volatile 變量是一種非常簡單但同時(shí)又非常脆弱的同步機(jī)制,它在某些情況下將提供優(yōu)于鎖的性能和伸縮性。如果嚴(yán)格遵循 volatile 的使用條件 —— 即變量真正獨(dú)立于其他變量和自己以前的值 —— 在某些情況下可以使用 volatile 代替 synchronized 來簡化代碼。然而,使用 volatile 的代碼往往比使用鎖的代碼更加容易出錯(cuò)。
另外如果不是很在意性能方面,并且希望實(shí)現(xiàn)簡潔明了的技術(shù)器功能,可以參考我博客內(nèi)的另一篇介紹AtomicInteger類的文章,該類可以實(shí)現(xiàn)原子性操作,從而保證線程安全:http://blog.csdn.net/roy_70/a...

參考文章:
Java 理論與實(shí)踐: 正確使用 Volatile 變量:
https://www.ibm.com/developer...

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/73042.html

相關(guān)文章

  • Java 多線程核心技術(shù)梳理(附源碼)

    摘要:本文對多線程基礎(chǔ)知識進(jìn)行梳理,主要包括多線程的基本使用,對象及變量的并發(fā)訪問,線程間通信,的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。源碼采用構(gòu)建,多線程這部分源碼位于模塊中。通知可能等待該對象的對象鎖的其他線程。 本文對多線程基礎(chǔ)知識進(jìn)行梳理,主要包括多線程的基本使用,對象及變量的并發(fā)訪問,線程間通信,lock的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。 寫在前面 花了一周時(shí)...

    Winer 評論0 收藏0
  • Java 多線程編程核心技術(shù)6—單例模式與多線程

    摘要:使用雙檢查機(jī)制來實(shí)現(xiàn)多線程環(huán)境中的延遲加載單例設(shè)計(jì)模式。類主要負(fù)責(zé)日期的轉(zhuǎn)換與格式化,但在多線程環(huán)境中,使用此類容易造成數(shù)據(jù)轉(zhuǎn)換及處理的不準(zhǔn)確,因?yàn)轭惒⒉皇蔷€程安全的。 立即加載就是使用類的時(shí)候已經(jīng)將對象創(chuàng)建完畢,常見的實(shí)現(xiàn)辦法就是直接new實(shí)例化。而立即加載從中文的語境來看,有著急、急迫的含義,所以也稱為餓漢模式。 package com.zxf.demo.singleton_0; ...

    TesterHome 評論0 收藏0
  • 事務(wù)與多線程的坑及調(diào)優(yōu)Tips

    摘要:起因及介紹在處理原始對賬文件的時(shí)候,我將數(shù)據(jù)歸類后批量存入相應(yīng)的表中。結(jié)論事務(wù)只能管著開啟事務(wù)的線程,其他子線程出了問題都感知不到,所以在多線程環(huán)境操作要慎重。高頻容易搞死服務(wù)器,低頻會阻塞自身程序。重試次數(shù)和超時(shí)時(shí)間根據(jù)業(yè)務(wù)情況設(shè)置。 起因及介紹 在處理原始對賬文件的時(shí)候,我將數(shù)據(jù)歸類后批量存入相應(yīng)的表中。在持久化的時(shí)候,用了parallelStream(),想著同時(shí)存入很多表這樣可...

    wums 評論0 收藏0
  • java中的Executors簡介與多線程在網(wǎng)站上逐步優(yōu)化的運(yùn)用案例

    摘要:的多線程機(jī)制可彌補(bǔ)拋出未檢查的異常,將終止線程執(zhí)行,此時(shí)會錯(cuò)誤的認(rèn)為任務(wù)都取消了。如果想要不保留,則需要設(shè)置,此時(shí)最小的就是線程池最大的線程數(shù)。 提供Executor的工廠類showImg(https://segmentfault.com/img/bVbj3Ei?w=2890&h=1480); 忽略了自定義的ThreadFactory、callable和unconfigurable相關(guān)...

    sunsmell 評論0 收藏0
  • python大佬養(yǎng)成計(jì)劃----進(jìn)程、線程與多進(jìn)程

    摘要:在一個(gè)進(jìn)程內(nèi)部,要同時(shí)干多件事,就需要同時(shí)運(yùn)行多個(gè)子任務(wù),我們把進(jìn)程內(nèi)的這些子任務(wù)稱為線程。總結(jié)一下,多任務(wù)的實(shí)現(xiàn)方式有三種多進(jìn)程模式多線程模式多進(jìn)程多線程模式線程是最小的執(zhí)行單元,而進(jìn)程由至少一個(gè)線程組成。 進(jìn)程與線程 很多同學(xué)都聽說過,現(xiàn)代操作系統(tǒng)比如Mac OS X,UNIX,Linux,Windows等,都是支持多任務(wù)的操作系統(tǒng)。 什么叫多任務(wù)呢?簡單地說,就是操作系統(tǒng)可以同時(shí)...

    taowen 評論0 收藏0

發(fā)表評論

0條評論

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