6 Java并發(fā)-3

第十一課 Volatile

例子


public class VolatileTest extends  Thread {

    /**
     * 使用了volatile秒紧,則1秒后,子線程會退出循環(huán)挨下,因為在主線程將isRunning置位為false
     */
    //private volatile boolean isRunning = true;
    
    /**
     * 不使用volatile熔恢,1秒后,主線程置位isRunning為false臭笆,但主線程對isRunning的修改對子線程不可見叙淌,子線程看見的還是true秤掌,循環(huán)繼續(xù)
     * 
     * 將setRunning方法設(shè)置為synchronized也可以達(dá)到volatile的效果,意思就是同步代碼塊保護的變量修改時也會直接刷新到主存
     * 
     */
    private boolean isRunning = true;
    
    public boolean isRunning(){
        return isRunning;
    }
    
    public void setRunning(boolean isRunning){
        this.isRunning= isRunning;
    }
    
    public void run(){
        System.out.println("進(jìn)入了run...............");
        while (isRunning){}
        System.out.println("isUpdated的值被修改為為false,線程將被停止了");
    }
    public static void main(String[] args) throws InterruptedException {
        VolatileTest volatileThread = new VolatileTest();
        volatileThread.start();
        Thread.sleep(1000);
        volatileThread.setRunning(false);   //停止線程
    }
}

Volatile和原子性沒什么直接關(guān)系
如果變量被同步代碼保護了鹰霍,就不必考慮volatile

怎么引出這個問題呢

public abstract class IntGenerator {
  private volatile boolean canceled = false;  
  public abstract int next();
  // Allow this to be canceled:
  public void cancel() { canceled = true; }
  public boolean isCanceled() { return canceled; }
} 
  • 分析
    • 看這個類的canceled字段机杜,在這里一個IntGenerator對象可以被多個EventChecker對象調(diào)用cancel()
    • 這樣就在每個EventChecker的線程里,保留了一份對canceled的本地緩存衅谷,這個本地緩存可能是每個CPU一個
    • 在每個線程里調(diào)用修改cenceled的值椒拗,首先會保存到本地緩存,然后也會同步到主存里获黔,據(jù)說這是規(guī)定蚀苛,必須的
    • 但是其他線程通過isCanceled()讀取它的值,是從本地緩存讀,沒被改變,即不可見茫负,它已經(jīng)看不見主存里的值了
    • 所以用volatile來修飾吓肋,保證每次對它的修改太雨,都會同步到主存的同時,也會對所有其他線程的內(nèi)存可見,或者就是保證對于volatile變量,不會在工作內(nèi)存中拷貝一份雌芽,都是在主存中讀寫
    • 整了半天,還是挺麻煩辨嗽,推薦首選用同步來解決問題世落,volatile適用于只有一個字段可變的情況

下面的內(nèi)容來自網(wǎng)頁:http://www.cnblogs.com/MOBIN/p/5407965.html?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

  • 摘要

    • Volatile是Java提供的一種弱同步機制,當(dāng)一個變量被聲明成volatile類型后編譯器不會將該變量的操作與其他內(nèi)存操作進(jìn)行重排序糟需。
    • 在某些場景下使用volatile代替鎖可以減少代碼量和使代碼更易閱讀
  • Volatile的特性

    • 可見性:當(dāng)一條線程對volatile變量進(jìn)行了修改操作時屉佳,其他線程能立即知道修改的值,即當(dāng)讀取一個volatile變量時總是返回最近一次寫入的值
    • 原子性:對于單個voatile變量其具有原子性(能保證long double類型的變量具有原子性)洲押,但對于i ++ 這類復(fù)合操作其不具有原子性(見下面分析)
  • Volatile使用的前提

    • 對變量的寫入操作不依賴變量的當(dāng)前值武花,或者能夠確保只有單一的線程修改變量的值
    • 該變量不會與其他狀態(tài)變量一起納入不變性條件中
    • 在訪問變量時不需要加鎖

原理:
原因:Java內(nèi)存模型(JMM)規(guī)定了所有的變量都存儲在主內(nèi)存中,主內(nèi)存中的變量為共享變量杈帐,
而每條線程都有自己的工作內(nèi)存体箕,線程的工作內(nèi)存保存了從主內(nèi)存拷貝的變量,
所有對變量的操作都在自己的工作內(nèi)存中進(jìn)行娘荡,完成后再刷新到主內(nèi)存中干旁,
回到例1驶沼,第18行號代碼主線程(線程main)雖然對isRunning的變量進(jìn)行了修改且有刷新
回主內(nèi)存中(《深入理解java虛擬機》中關(guān)于主內(nèi)存與工作內(nèi)存的交互協(xié)議提到變量在工作 內(nèi)存中改變后必須將該變化同步回主內(nèi)存)炮沐,但volatileThread線程讀的仍是自己工作內(nèi)存
的舊值導(dǎo)致出現(xiàn)多線程的可見性問題,解決辦法就是給isRunning變量加上volatile關(guān)鍵字回怜。

  • volatile內(nèi)存語義總結(jié)如下
    • 當(dāng)線程對volatile變量進(jìn)行寫操作時大年,會將修改后的值刷新回主內(nèi)存
    • 當(dāng)線程對volatile變量進(jìn)行讀操作時换薄,會先將自己工作內(nèi)存中的變量置為無效,之后再通過主內(nèi)存拷貝新值到工作內(nèi)存中使用翔试。
  • Synchronized與volatile區(qū)別
    • volatile只能修飾變量轻要,而synchronized可以修改變量,方法以及代碼塊
    • volatile在多線程中不會存在阻塞問題垦缅,synchronized會存在阻塞問題
    • volatile能保證數(shù)據(jù)的可見性冲泥,但不能完全保證數(shù)據(jù)的原子性,synchronized即保證了數(shù)據(jù)的可見性也保證了原子性
    • volatile解決的是變量在多個線程之間的可見性壁涎,而sychroized解決的是多個線程之間訪問資源的同步性

第十二課 java提供的并發(fā)構(gòu)件

1 CountDownLatch

package com.cowthan.concurrent.c14;

//: concurrency/CountDownLatchDemo.java
import java.util.concurrent.*;
import java.util.*;

// Performs some portion of a task:
class TaskPortion implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private static Random rand = new Random(47);
    private final CountDownLatch latch;

    TaskPortion(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        try {
            doWork();
            latch.countDown();
        } catch (InterruptedException ex) {
            // Acceptable way to exit
        }
    }

    public void doWork() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000));
        System.out.println(this + "completed");
    }

    public String toString() {
        return String.format("%1$-3d ", id);
    }
}

// Waits on the CountDownLatch:
class WaitingTask implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final CountDownLatch latch;

    WaitingTask(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        try {
            latch.await();
            System.out.println("Latch barrier passed for " + this);
        } catch (InterruptedException ex) {
            System.out.println(this + " interrupted");
        }
    }

    public String toString() {
        return String.format("WaitingTask %1$-3d ", id);
    }
}

public class CountDownLatchDemo {
    static final int SIZE = 100;

    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool();
        // All must share a single CountDownLatch object:
        CountDownLatch latch = new CountDownLatch(SIZE);
        for (int i = 0; i < 10; i++)
            exec.execute(new WaitingTask(latch));
        for (int i = 0; i < SIZE; i++)
            exec.execute(new TaskPortion(latch));
        System.out.println("Launched all tasks");
        exec.shutdown(); // Quit when all tasks complete
    }
} /* (Execute to see output) */// :~

  • 適用于:

    • 一組子任務(wù)并行執(zhí)行凡恍,另一組任務(wù)等待著一組完成才進(jìn)行,或等待某個條件完成才進(jìn)行
      • 并行執(zhí)行的任務(wù)數(shù)怔球,或者等待的這個條件嚼酝,可以抽象成倒數(shù),倒數(shù)到0竟坛,則另一組任務(wù)就可以繼續(xù)執(zhí)行
    • 一個任務(wù)會被分解成多個子任務(wù)x闽巩,y,z
    • 其中一個子任務(wù)B會等待其他幾個子任務(wù)完成才會繼續(xù)執(zhí)行
    • 所以提供一個CountDownLatch對象担汤,并設(shè)置初始值
      • 任務(wù)B在CountDownLatch對象上await:latch.await();
      • 每完成一個子任務(wù)涎跨,就在CountDownLatch對象上倒數(shù)一次:latch.countDown();
      • 直到倒數(shù)到0,await的對象就會被喚醒
      • 任務(wù)B可以有多個
  • 限制:

    • 只能用一次崭歧,如果要用多次六敬,參考CyclicBarrier

2 CyclicBarrier

例子

package com.cowthan.concurrent.c14;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class Horse implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private int strides = 0;
    private static Random rand = new Random(47);
    private static CyclicBarrier barrier;

    public Horse(CyclicBarrier b) {
        barrier = b;
    }

    public synchronized int getStrides() {
        return strides;
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    strides += rand.nextInt(3); // Produces 0, 1 or 2
                }
                barrier.await();
            }
        } catch (InterruptedException e) {
            // A legitimate way to exit
        } catch (BrokenBarrierException e) {
            // This one we want to know about
            throw new RuntimeException(e);
        }
    }

    public String toString() {
        return "Horse " + id + " ";
    }

    public String tracks() {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < getStrides(); i++)
            s.append("*");
        s.append(id);
        return s.toString();
    }
}

class HorseRace {
    static final int FINISH_LINE = 75;
    private List<Horse> horses = new ArrayList<Horse>();
    private ExecutorService exec = Executors.newCachedThreadPool();
    private CyclicBarrier barrier;

    public HorseRace(int nHorses, final int pause) {
        barrier = new CyclicBarrier(nHorses, new Runnable() {
            public void run() {
                StringBuilder s = new StringBuilder();
                for (int i = 0; i < FINISH_LINE; i++)
                    s.append("="); // The fence on the racetrack
                System.out.println(s);
                for (Horse horse : horses)
                    System.out.println(horse.tracks());
                for (Horse horse : horses)
                    if (horse.getStrides() >= FINISH_LINE) {
                        System.out.println(horse + "won!");
                        exec.shutdownNow();
                        return;
                    }
                try {
                    TimeUnit.MILLISECONDS.sleep(pause);
                } catch (InterruptedException e) {
                    System.out.println("barrier-action sleep interrupted");
                }
            }
        });
        for (int i = 0; i < nHorses; i++) {
            Horse horse = new Horse(barrier);
            horses.add(horse);
            exec.execute(horse);
        }
    }

}

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        int nHorses = 3;  //幾匹馬
        int pause = 200;  //等多久走一步
        new HorseRace(nHorses, pause);
    }
}

  • 適用于:

    • 某個人物要等待多個任務(wù)并行進(jìn)行,直到都完成驾荣,才會執(zhí)行
    • 可以重用
    • 不得不說外构,CyclicBarrier還有點不好理解,看了demo代碼還是沒整明白
      • 怎么是horse在barrier上await呢
      • CyclicBarrier構(gòu)造怎么還得傳入必須await的線程個數(shù)呢
  • 介紹

    • 構(gòu)造:barrier = new CyclicBarrier(n, new Runnable(){})
      • 參數(shù)1:計數(shù)值播掷,當(dāng)有線程在barrier上await時审编,計數(shù)減一,n個線程都await了歧匈,計數(shù)就成0了垒酬,柵欄動作就會執(zhí)行
      • 參數(shù)2:叫做柵欄動作,計數(shù)到0時件炉,會自動執(zhí)行
  • CyclicBarrierDemo講解:

    • 柵欄動作做的事情是:
      • 打印線路勘究,打印終點
      • 打印每匹馬當(dāng)前的位置
      • 判斷是否有馬走到終點,有則提示奪冠斟冕,并結(jié)束所有線程(shutdownNow)
    • 柵欄動作執(zhí)行完后口糕,計數(shù)又會重置,此時
      • 每匹馬再向前走一步磕蛇,距離是隨機數(shù)
      • 走完之后景描,await一下
      • 所有馬都走完一步十办,await倒數(shù)計數(shù)值又是0了,再激活柵欄動作
    • 如此循環(huán)
    • 所以超棺,柵欄動作等所有子任務(wù)都await了向族,才運行,此時所有子任務(wù)都阻塞棠绘,子任務(wù)等柵欄動作完成件相,計數(shù)自動重置,再被喚醒
  • 總結(jié):

    • 構(gòu)造時氧苍,傳入計數(shù)值和柵欄動作
    • 計數(shù)值減一操作由子任務(wù)的await完成
    • 柵欄動作在計數(shù)值為0時激活适肠,并且運行完會自動重置計數(shù)值,并喚醒await的線程們
  • 更多:
    • 思考:如果沒有CyclicBarrier候引,仿真賽馬你會怎么實現(xiàn)侯养?
    • 你的實現(xiàn)會考慮起始和終結(jié)的情況嗎?宣布奪冠之后澄干,所有的馬都能立即停止前進(jìn)嗎逛揩?統(tǒng)計開始和結(jié)束時,所有馬的狀態(tài)保持前后一致嗎麸俘?
    • 提示1:CyclicBarrier的子任務(wù)辩稽,會在await上等待柵欄動作的結(jié)束,并且await是可以被interrupt的
    • 提示2:賽場統(tǒng)計是由柵欄動作完成的从媚,此動作會在每一批馬都前進(jìn)一步之后逞泄,所有馬都await,柵欄動作開始統(tǒng)計
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拜效,一起剝皮案震驚了整個濱河市喷众,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌紧憾,老刑警劉巖到千,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赴穗,居然都是意外死亡憔四,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門般眉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來了赵,“玉大人,你說我怎么就攤上這事甸赃∈裂矗” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵辑奈,是天一觀的道長苛茂。 經(jīng)常有香客問我已烤,道長鸠窗,這世上最難降的妖魔是什么妓羊? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮稍计,結(jié)果婚禮上躁绸,老公的妹妹穿的比我還像新娘。我一直安慰自己臣嚣,他們只是感情好净刮,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著硅则,像睡著了一般淹父。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怎虫,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天暑认,我揣著相機與錄音,去河邊找鬼大审。 笑死蘸际,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的徒扶。 我是一名探鬼主播粮彤,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姜骡!你這毒婦竟也來了导坟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤圈澈,失蹤者是張志新(化名)和其女友劉穎乍迄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體士败,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡闯两,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了谅将。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漾狼。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖饥臂,靈堂內(nèi)的尸體忽然破棺而出逊躁,到底是詐尸還是另有隱情,我是刑警寧澤隅熙,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布稽煤,位于F島的核電站核芽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酵熙。R本人自食惡果不足惜轧简,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匾二。 院中可真熱鬧哮独,春花似錦、人聲如沸察藐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽分飞。三九已至悴务,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間譬猫,已是汗流浹背讯檐。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留删窒,地道東北人裂垦。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像肌索,于是被迫代替她去往敵國和親蕉拢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容