【Java核心基礎(chǔ)知識(shí)】05 - 多線程并發(fā)(4)

多線程知識(shí)點(diǎn)目錄

多線程并發(fā)(1)- http://www.reibang.com/p/8fcfcac74033
多線程并發(fā)(2)-http://www.reibang.com/p/a0c5095ad103
多線程并發(fā)(3)-http://www.reibang.com/p/c5c3bbd42c35
多線程并發(fā)(4)-http://www.reibang.com/p/e45807a9853e
多線程并發(fā)(5)-http://www.reibang.com/p/5217588d82ba
多線程并發(fā)(6)-http://www.reibang.com/p/d7c888a9c03c

十一太颤、Java阻塞隊(duì)列原理

11.1 線程阻塞的兩種情況:

  1. 當(dāng)隊(duì)列中沒有數(shù)據(jù)的情況下,消費(fèi)者端的所有線程都會(huì)被自動(dòng)阻塞(掛起)犬金,直到有數(shù)據(jù)放入隊(duì)列护奈。


    情況1
  2. 當(dāng)隊(duì)列中填滿數(shù)據(jù)的情況下单鹿,生產(chǎn)者端的所有線程都會(huì)被自動(dòng)阻塞(掛起)园匹,直到隊(duì)列中有空的為止,線程被自動(dòng)喚醒戚丸。


    情況2

11.2 阻塞隊(duì)列的主要方法

阻塞隊(duì)列的主要辦法
插入操作
  1. add(E paramE):將指定元素插入此隊(duì)列中(如果立即可行且不會(huì)違反容量限制)划址,成功時(shí)返回true,如果當(dāng)前沒有可用空間限府,則拋出IllegalStateException夺颤。如果該元素使NULL,則會(huì)拋出NullPointException異常胁勺。

  2. offer(E paramE):將指定元素插入此隊(duì)列中(如果立即可行且不會(huì)違反容量限制)世澜,成功時(shí)返回true,如果當(dāng)前沒有可用空間署穗,則返回false寥裂。

  3. put(E paramE) throws InterruptedException:將指定元素插入次隊(duì)列中嵌洼,將等待可用的空間(如果有必要),如果隊(duì)列滿了封恰,則線程阻塞等待咱台。

  4. offer(E o, long timeout, TimeUnit unit):可以設(shè)定等待的時(shí)間,如果在指定的時(shí)間內(nèi)俭驮,還不能往隊(duì)列中加入BlockingQueue,則返回失敗春贸。

獲取數(shù)據(jù)操作
  1. poll(time):取走BlockingQueue中排在首位的對(duì)象混萝,若不能立即取出,則可以等time參數(shù)規(guī)定的時(shí)間萍恕,取不到時(shí)返回null逸嘀。

  2. poll(long timeout, TimeUnit unit):從BlockingQueue取出一個(gè)隊(duì)首的對(duì)象,如果在指定時(shí)間內(nèi)允粤,隊(duì)列一單有數(shù)據(jù)可取崭倘,則立即返回隊(duì)列中的數(shù)據(jù)。否則直到時(shí)間超時(shí)還沒數(shù)據(jù)可取类垫,返回失敗司光。

  3. take():取走BlockingQueue里排在首位的對(duì)象,若BlockingQueue為空悉患,阻斷進(jìn)入等待狀態(tài)残家,直到BlockingQueue有新的數(shù)據(jù)被加入。

  4. drainTo():一次性從BlockingQueue獲取所有可用的數(shù)據(jù)對(duì)象(還可以指定獲取數(shù)據(jù)的個(gè)數(shù))售躁,通過該方法坞淮,可以提升獲取數(shù)據(jù)效率;不需要多次分批加鎖或釋放鎖陪捷。

11.3 Java中的阻塞隊(duì)列

    1. ArrayBlockingQueue:由數(shù)據(jù)結(jié)構(gòu)組成的有界阻塞隊(duì)列回窘。

這是一個(gè)基于數(shù)組的實(shí)現(xiàn)阻塞隊(duì)列。它內(nèi)部使用了一個(gè)數(shù)組來存儲(chǔ)元素市袖,按照先進(jìn)先出(FIFO)的原則對(duì)元素進(jìn)行排序啡直,并且可以設(shè)置最大容量。當(dāng)隊(duì)列滿時(shí)凌盯,如果有線程試圖向隊(duì)列中插入元素付枫,線程將會(huì)被阻塞直到隊(duì)列中有空間;當(dāng)隊(duì)列空時(shí)驰怎,如果有線程試圖從隊(duì)列中刪除元素阐滩,線程將會(huì)被阻塞直到隊(duì)列中有元素。
默認(rèn)情況下不保證訪問者公平的訪問隊(duì)列县忌,所謂公平訪問隊(duì)列是指阻塞的所有生產(chǎn)者線程或消費(fèi)者線程掂榔,當(dāng)隊(duì)列可用時(shí)继效,可以按照阻塞的先后順序訪問隊(duì)列,即先阻塞的生產(chǎn)者線程装获,可以先往隊(duì)列里插入元素瑞信,先阻塞的消費(fèi)者線程,可以先從隊(duì)列里獲取元素穴豫。

    1. LinkedBlockingQueue:由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列凡简。

這是一個(gè)基于鏈表的實(shí)現(xiàn)阻塞隊(duì)列。它內(nèi)部使用了一個(gè)鏈表來存儲(chǔ)元素精肃,按照先進(jìn)先出(FIFO)的原則對(duì)元素進(jìn)行排序秤涩。與ArrayBlockingQueue不同的是,LinkedBlockingQueue的大小可以動(dòng)態(tài)增長(zhǎng)司抱。當(dāng)隊(duì)列滿時(shí)筐眷,如果有線程試圖向隊(duì)列中插入元素,線程將會(huì)被阻塞直到隊(duì)列中有空間习柠;當(dāng)隊(duì)列空時(shí)匀谣,如果有線程試圖從隊(duì)列中刪除元素,線程將會(huì)被阻塞直到隊(duì)列中有元素资溃。
而 LinkedBlockingQueue 之所以能夠高效的處理并發(fā)數(shù)據(jù)武翎,還因?yàn)槠鋵?duì)于生產(chǎn)者端和消費(fèi)者端分別采用了獨(dú)立的鎖來控制數(shù)據(jù)同步,這也意味著在高并發(fā)的情況下生產(chǎn)者和消費(fèi)者可以并行地操作隊(duì)列中的數(shù)據(jù)肉拓,以此來提高整個(gè)隊(duì)列的并發(fā)性能后频。

    1. PriorityBlockingQueue:支持優(yōu)先級(jí)排序的無界阻塞隊(duì)列。

這是一個(gè)支持優(yōu)先級(jí)排序的阻塞隊(duì)列暖途,默認(rèn)情況下元素采取自然順序升序排列卑惜。它內(nèi)部使用了一個(gè)優(yōu)先級(jí)堆來存儲(chǔ)元素,使得高優(yōu)先級(jí)的元素總是先于低優(yōu)先級(jí)的元素出隊(duì)驻售,需要注意的是不能保證同優(yōu)先級(jí)元素的順序露久。

    1. DelayQueue:使用優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的無界阻塞隊(duì)列。

這是一個(gè)使用優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的阻塞隊(duì)列欺栗。它內(nèi)部使用了一個(gè)優(yōu)先級(jí)堆來存儲(chǔ)元素毫痕,但與PriorityBlockingQueue不同的是,DelayQueue中的元素只有當(dāng)其指定的延遲時(shí)間到了迟几,才能從隊(duì)列中刪除消请。

    1. SynchronizedQueue:不存儲(chǔ)元素的阻塞隊(duì)列。

這是一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列类腮。它的所有操作都是對(duì)另一個(gè)阻塞隊(duì)列的操作進(jìn)行同步臊泰,也就是說,它本身并不存儲(chǔ)任何元素蚜枢。每一個(gè) put 操作必須等待一個(gè) take 操作缸逃,否則不能繼續(xù)添加元素针饥。
SynchronousQueue 的 吞 吐 量 高 于 LinkedBlockingQueue 和 ArrayBlockingQueue。

    1. LinkedTransferQueue:由鏈表結(jié)構(gòu)組成的無界阻塞隊(duì)列需频。

這是一個(gè)基于鏈表的無界阻塞隊(duì)列丁眼。它內(nèi)部使用了一個(gè)鏈表來存儲(chǔ)元素。與LinkedBlockingQueue不同的是昭殉,LinkedTransferQueue的大小可以動(dòng)態(tài)增長(zhǎng)且支持多生產(chǎn)者多消費(fèi)者模式苞七。

    1. LinkedBlockingDeque:由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列。

這是一個(gè)基于鏈表的雙向阻塞隊(duì)列挪丢。它內(nèi)部使用了一個(gè)鏈表來存儲(chǔ)元素莽鸭,支持在兩端插入和刪除元素。
雙向隊(duì)列指的你可以從隊(duì)列的兩端插入和移出元素吃靠。雙端隊(duì)列因?yàn)槎嗔艘粋€(gè)操作隊(duì)列的入口,在多線程同時(shí)入隊(duì)時(shí)足淆,也就減少了一半的競(jìng)爭(zhēng)巢块。相比其他的阻塞隊(duì)列,LinkedBlockingDeque多了addFirst巧号,addLast族奢,offerFirst,offerLast丹鸿,peekFirst越走,peekLast等方法

十二、volatile關(guān)鍵字的作用

Java語言提供了一種稍弱的同步機(jī)制靠欢,即volatile變量廊敌,用來確保將變量的更新操作通知到其他線程。volatile變量具備兩種特性:①變量可見性门怪;②禁止重排骡澈。volatile變量不會(huì)被緩存在寄存器或者其他處理器不可見的地方,因此在讀取volatile類型的變量是總是會(huì)返回最新寫入的值掷空。

  • ① 變量可見性
    其一是保證該變量對(duì)所有線程可見肋殴,這里的可見性指的是當(dāng)一個(gè)線程修改了變量的值,那么新的值對(duì)于其他線程時(shí)可以立即獲取的坦弟。
  • ② 禁止重排
    volatile禁止了指令重排护锤。

比Synchronized更輕量級(jí)的同步鎖

在訪問volatile變量時(shí)不會(huì)執(zhí)行加鎖操作,因此也就不會(huì)執(zhí)行線程阻塞酿傍,因此烙懦,volatile變量是一種比Synchronized關(guān)鍵字更輕量級(jí)的同步機(jī)制。volatile適合這種場(chǎng)景:一個(gè)變量被多個(gè)線程共享拧粪,現(xiàn)成直接給這個(gè)變量賦值修陡。


不同類型變量讀取

當(dāng)對(duì)非volatile變量進(jìn)行讀寫的時(shí)候沧侥,每個(gè)線程先從內(nèi)存拷貝變量到CPU緩存中。如果計(jì)算機(jī)有多個(gè)CPU魄鸦,每個(gè)線程可能在不同的CPU上被處理宴杀,這意味著每個(gè)線程可以拷貝到不同的CPU Cache中。而聲明變量是volatile的拾因,JVM保證了每次讀變量都從內(nèi)存中讀旺罢,跳過CPU Cache這一步。

使用場(chǎng)景

volatile變量的單次讀/寫操作可以保證原子性绢记,如long和double類型變量扁达,但是,不能保證i++這種操作的原子性蠢熄,因?yàn)楸举|(zhì)上i++是讀跪解、寫兩次操作。在某些場(chǎng)景下可以代替Synchronized签孔。但是叉讥,volatile不能完全取代Synchronized,只有在一些特殊的場(chǎng)景下才能適用饥追⊥疾郑總的來說,必須同時(shí)滿足下面兩個(gè)條件才能保證在并發(fā)環(huán)境的線程安全:

  1. 對(duì)變量的寫操作不依賴于當(dāng)前值(i++依賴當(dāng)前值但绕,不能保證線程安全)救崔,或者說是單純的變量賦值(boolean flag=true)
  2. 該變量沒有包含在具有其他變量的不變式中,也就是說捏顺,不同的volatile變量之間六孵,不能互相依賴。只有在狀態(tài)真正獨(dú)立于程序內(nèi)其他內(nèi)容時(shí)幅骄,才能使用volatile狸臣。

十三、如何在兩個(gè)線程之間共享數(shù)據(jù)

Java里面進(jìn)行多線程通訊的主要方式就是共享內(nèi)存的方式昌执,共享內(nèi)存主要的關(guān)注點(diǎn)有:①可見性烛亦;②有序性;③原子性懂拾。
Java內(nèi)存模型(JMM)解決了可見性和有序性的問題煤禽,而鎖解決了原子性的問題,理想情況下我們希望坐到“同步”和“互斥”岖赋。有以下常規(guī)實(shí)現(xiàn)方法:

13.1 將數(shù)據(jù)抽象成一個(gè)類檬果,并將數(shù)據(jù)的操作作為這個(gè)類的方法

將數(shù)據(jù)抽象成一個(gè)類,并將對(duì)這個(gè)數(shù)據(jù)的操作作為這個(gè)類的方法,這么設(shè)計(jì)可以做到同步选脊,只要在方法上加上“Synchronized”

public class MyThread {
    public static void main(String[] args) throws InterruptedException {
        MyData myData = new MyData();
        Runnable add = new AddRunnable(myData);
        Runnable dec = new DecRunnable(myData);
        for (int i = 0; i < 2; i++) {
            new Thread(add).start();
            new Thread(dec).start();
        }
    }
}

class MyData {
    private int j = 0;

    public synchronized void add() {
        j++;
        System.out.println("[add()]線程" + Thread.currentThread().getName() + "的j的值為:" + j);
    }

    public synchronized void dec() {
        j--;
        System.out.println("[dec()]線程" + Thread.currentThread().getName() + "的j的值為:" + j);
    }

    public int getData() {
        return j;
    }
}

class AddRunnable implements Runnable {
    private MyData myData;

    public AddRunnable(MyData data) {
        this.myData = data;
    }

    public void run() {
        myData.add();
    }
}

class DecRunnable implements Runnable {
    private MyData myData;

    public DecRunnable(MyData data) {
        this.myData = data;
    }

    public void run() {
        myData.dec();
    }
}

13.2 Runnable對(duì)象作為一個(gè)類的內(nèi)部類

將Runnable對(duì)象作為一個(gè)類的內(nèi)部類杭抠,共享數(shù)據(jù)作為這個(gè)類的成員變量,每個(gè)線程對(duì)共享數(shù)據(jù)的操作方法也封裝在外部類恳啥,以便實(shí)現(xiàn)對(duì)數(shù)據(jù)的各個(gè)操作的同步和互斥偏灿,作為內(nèi)部類的各個(gè)Runnable對(duì)象調(diào)用外部類的這些方法。

public class MyThread {
    public static void main(String[] args) throws InterruptedException {
        final MyData myData = new MyData();
        for (int i = 0; i < 2; i++) {
            new Thread(myData::add).start();
            new Thread(myData::dec).start();
        }
    }
}


class MyData {
    private int j = 0;

    public synchronized void add() {
        j++;
        System.out.println("[add()]線程" + Thread.currentThread().getName() + "的j的值為:" + j);
    }

    public synchronized void dec() {
        j--;
        System.out.println("[dec()]線程" + Thread.currentThread().getName() + "的j的值為:" + j);
    }

    public int getData() {
        return j;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钝的,一起剝皮案震驚了整個(gè)濱河市翁垂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌硝桩,老刑警劉巖沿猜,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異碗脊,居然都是意外死亡啼肩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門衙伶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疟游,“玉大人,你說我怎么就攤上這事痕支。” “怎么了蛮原?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵卧须,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我儒陨,道長(zhǎng)花嘶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任蹦漠,我火速辦了婚禮椭员,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘笛园。我一直安慰自己隘击,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布研铆。 她就那樣靜靜地躺著埋同,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棵红。 梳的紋絲不亂的頭發(fā)上凶赁,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼虱肄。 笑死致板,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咏窿。 我是一名探鬼主播斟或,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼翰灾!你這毒婦竟也來了缕粹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤纸淮,失蹤者是張志新(化名)和其女友劉穎平斩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咽块,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绘面,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了侈沪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揭璃。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亭罪,靈堂內(nèi)的尸體忽然破棺而出瘦馍,到底是詐尸還是另有隱情,我是刑警寧澤应役,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布情组,位于F島的核電站,受9級(jí)特大地震影響箩祥,放射性物質(zhì)發(fā)生泄漏院崇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一袍祖、第九天 我趴在偏房一處隱蔽的房頂上張望底瓣。 院中可真熱鬧,春花似錦蕉陋、人聲如沸捐凭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柑营。三九已至,卻和暖如春村视,著一層夾襖步出監(jiān)牢的瞬間官套,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奶赔,地道東北人惋嚎。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像站刑,于是被迫代替她去往敵國(guó)和親另伍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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