摘要:本人郵箱歡迎轉(zhuǎn)載轉(zhuǎn)載請(qǐng)注明網(wǎng)址代碼已經(jīng)全部托管有需要的同學(xué)自行下載引言做的同學(xué)們或多或少的接觸過集合框架在集合框架中大多的集合類是線程不安全的比如我們常用的等等我們寫一個(gè)例子看為什么說是不安全的例子證明是線程不安全的我們開啟個(gè)線程每個(gè)線程向
引言本人郵箱:
歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明網(wǎng)址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代碼已經(jīng)全部托管github有需要的同學(xué)自行下載
做java的同學(xué)們或多或少的接觸過Java集合框架.在java集合框架中,大多的集合類是線程不安全的.比如我們常用的ArrayList等等.我們寫一個(gè)例子看,為什么說ArrayList是不安全的.
例子1 證明ArrayList是線程不安全的我們開啟100個(gè)線程.每個(gè)線程向List加100個(gè)數(shù)據(jù),那么當(dāng)所有線程執(zhí)行完成之后應(yīng)該是10000條,然后就對(duì)比一下結(jié)果,看看是否為10000條.
public class Demo1 { public static void main(String[] args) throws InterruptedException { Listlist = new ArrayList<>(); Thread[] threads = new Thread[100]; for (int i = 0; i < threads.length; i ++){ threads[i] = new Thread(() -> { for (int j = 0;j < 100; j ++){ list.add(Thread.currentThread().getName() + ":" + j); } }); threads[i].start(); } for (Thread thread : threads){ thread.join(); } System.out.println(list.size()); } }
thread.join(); 是讓主線程等待所有的子線程執(zhí)行完,才執(zhí)行接下來的語句
運(yùn)行結(jié)果為9979,而且每次運(yùn)行結(jié)果還不一定是這個(gè)數(shù).
當(dāng)然,我們可以通過學(xué)過的知識(shí),在執(zhí)行list.add是給它加鎖,比如將list.add(Thread.currentThread().getName() + ":" + j);改為synchronized (list){list.add(Thread.currentThread().getName() + ":" + j);}這樣就能保證線程同步了.
可直接用于并發(fā)的集合類其實(shí)java中已經(jīng)提供了可直接用于并發(fā)的集合類,它們可以在多線程中進(jìn)行CURD1操作,而且不需要程序員手動(dòng)加lock或synchronized來保證同步.一般來說,它們分以下兩種:
阻塞式集合(Blocking Collection): 這類集合一般在添加或刪除數(shù)據(jù),如果集合已滿或?yàn)榭諘r(shí),則調(diào)用添加和刪除方法的線程會(huì)被阻塞,直接該方法可以成功執(zhí)行
非阻塞式集合(Non-Blocking Collection):這類集合一般在添加或刪除數(shù)據(jù),如果方法不能立即執(zhí)行時(shí),則會(huì)返回Null或拋出異常,但調(diào)用該方法的線程不會(huì)被阻塞.
這節(jié)課我將重點(diǎn)講ArrayBlockingQueue,首先先看一下ArrayBlockingQueue的api,以及區(qū)分這些的差別
add(E),offer(E),pub(E)都是這隊(duì)列尾部加入元素E,如果隊(duì)列不滿,則加入成功,并立即返回.如果隊(duì)列滿了,那么
add會(huì)拋出IllegalStateException異常
offer立刻返回false
put會(huì)讓調(diào)用的線程一直等待,直到方法執(zhí)行成功
offer(E e, long timeout, TimeUnit unit),offer另一種方法,當(dāng)集合已滿,如果等待時(shí)間小于等于0,那么會(huì)離開返回false,否則等到指定的時(shí)間
poll(),take(),獲取隊(duì)列的數(shù)據(jù),如果隊(duì)列為空,那么
poll 立刻返回null
take 線程等待,直到獲取到數(shù)據(jù),或被中斷
poll(long timeout, TimeUnit unit),如隊(duì)列為空,當(dāng)指定時(shí)間小于等于,立刻返回null,否則等待指定的時(shí)間
peek(): 看一下隊(duì)列當(dāng)前的數(shù)據(jù),如果隊(duì)列為空,則立即返回null
例子之前我們寫過山治和路飛的故事,在(十二)java多線程之Exchanger的例子中,其實(shí)山治和路飛是一個(gè)簡(jiǎn)單的生產(chǎn)者-消費(fèi)者模式,只是山治和路飛都要等對(duì)方吃完或做完一個(gè)才能繼續(xù)下一個(gè).現(xiàn)在路飛想出另一個(gè)辦法,在廚房和餐桌之間弄一個(gè)傳送帶,山治把食物做好之后,直接放傳送帶上,路飛就直接從傳送帶拿食物.傳送帶最多只能放10個(gè)食物.ok,開始編碼..
Food 沒有改.
LuFeiRunnable改為
public class LuFeiRunnable implements Runnable{ ArrayBlockingQueuequeue; Random random = new Random(); public LuFeiRunnable(ArrayBlockingQueue queue) { this.queue = queue; } @Override public void run() { while (true){ try { String take = queue.take(); System.out.println("-->路飛拿到 " + take); Thread.sleep(random.nextInt(500)); System.out.println("-->路飛吃完 " + take); } catch (InterruptedException e) { e.printStackTrace(); } } } }
ShanZhiRunnable改為
public class ShanZhiRunnable implements Runnable{ ArrayBlockingQueuequeue; Random random = new Random(); public ShanZhiRunnable(ArrayBlockingQueue queue) { this.queue = queue; } @Override public void run() { while (true){ try { String food = Food.getRandomFood(); System.out.println("==>山治開始做 " + food); Thread.sleep(random.nextInt(500)); System.out.println("==>山治做好了 " + food); queue.put(food); } catch (InterruptedException e) { e.printStackTrace(); } } } }
測(cè)試類改為:
public class TestMain { public static void main(String[] args) { ArrayBlockingQueuequeue = new ArrayBlockingQueue<>(10); new Thread(new LuFeiRunnable(queue)).start(); new Thread(new ShanZhiRunnable(queue)).start(); } }
運(yùn)行結(jié)果:
==>山治開始做 椒麻雞 ==>山治做好了 椒麻雞 ==>山治開始做 牛腩煲 -->路飛拿到 椒麻雞 ==>山治做好了 牛腩煲 ==>山治開始做 豆苗炒蝦片 ==>山治做好了 豆苗炒蝦片 ==>山治開始做 火鞭牛肉 -->路飛吃完 椒麻雞 -->路飛拿到 牛腩煲 ==>山治做好了 火鞭牛肉 ==>山治開始做 桃酥雞糕 -->路飛吃完 牛腩煲 -->路飛拿到 豆苗炒蝦片 ==>山治做好了 桃酥雞糕 ==>山治開始做 雞湯煮干絲 -->路飛吃完 豆苗炒蝦片 -->路飛拿到 火鞭牛肉 ==>山治做好了 雞湯煮干絲 ==>山治開始做 菊花豬肝湯 -->路飛吃完 火鞭牛肉 -->路飛拿到 桃酥雞糕 ==>山治做好了 菊花豬肝湯 ==>山治開始做 清香炒悟雞 -->路飛吃完 桃酥雞糕 -->路飛拿到 雞湯煮干絲 -->路飛吃完 雞湯煮干絲 -->路飛拿到 菊花豬肝湯 ==>山治做好了 清香炒悟雞 ==>山治開始做 槐花豬腸湯 -->路飛吃完 菊花豬肝湯 -->路飛拿到 清香炒悟雞 -->路飛吃完 清香炒悟雞 ==>山治做好了 槐花豬腸湯 ==>山治開始做 瑞士排骨 -->路飛拿到 槐花豬腸湯 -->路飛吃完 槐花豬腸湯 ==>山治做好了 瑞士排骨 -->路飛拿到 瑞士排骨 ==>山治開始做 芝麻魚球 -->路飛吃完 瑞士排骨
山治和路飛都很happy,各自都在做自己喜歡做的事情,但是,如果路飛吃到一定階段,吃不下了,會(huì)發(fā)生什么呢?比如把LuFeiRunnable中的Thread.sleep(random.nextInt(500));改為Thread.sleep(random.nextInt(5000));,然后運(yùn)行結(jié)果,會(huì)發(fā)現(xiàn)山治做菜速度也變慢了,因?yàn)閭魉蛶У氖澄锓挪幌?山治必須等路飛吃掉一些,這樣傳送帶才能放下食物.
擴(kuò)展這就想我們?nèi)ャy行排隊(duì),醫(yī)院取號(hào),或者去各種部門排隊(duì)辦業(yè)務(wù)的時(shí)候.如果人來的太多了,那么他們一般的做法就是沒號(hào),要么在網(wǎng)上預(yù)約,要么下次早點(diǎn)來.這就是生產(chǎn)者(排隊(duì)的人)生產(chǎn)太快,消費(fèi)者(銀行,醫(yī)院等)消費(fèi)太慢了
ArrayBlockingQueue的父類是BlockingQueue,通過查找BlockingQueue的子類,我們能找到以下這幾個(gè)類.
SynchronousQueue跟ArrayBlockingQueue類似,只不過它除了可以用put,offer,take,add,poll,take這幾個(gè)方法外,其余的例如isEmpty,size,clear,contains,remove等等,都是無效的,大部分都是直接返回一個(gè)固定值.這是因?yàn)樗且粋€(gè)沒有容量的隊(duì)列.甚至連一個(gè)容量都沒有.因此在每次做插入操作的時(shí)候,都必須等其他線程做刪除操作.
LinkedBlockingQueue跟ArrayBlockingQueue類似,只是ArrayBlockingQueue是通過數(shù)組的方式實(shí)現(xiàn)隊(duì)列,而LinkedBlockingQueue是通過列表的方式實(shí)現(xiàn)隊(duì)列.
LinkedBlockingDeque跟LinkedBlockingQueue一樣是用鏈表實(shí)現(xiàn)隊(duì)形,只是LinkedBlockingDeque為雙向鏈表,可以在頭部或尾部進(jìn)行添加和刪除操作.
add*,offer*,put*這些增加操作跟LinkedBlockingQueue和LinkedBlockingQueue的add,offer,put是類似的,如果這些方法不帶*,則都是等價(jià)與*Last
poll*,take*這些獲取數(shù)據(jù)操作跟LinkedBlockingQueue和LinkedBlockingQueue的poll,take類似的,如果不帶*,則等價(jià)于*Frist
打賞在學(xué)習(xí)中,我們要能舉一反三.這樣我們就會(huì)學(xué)的比較快.
如果覺得我的文章寫的還過得去的話,有錢就捧個(gè)錢場(chǎng),沒錢給我捧個(gè)人場(chǎng)(幫我點(diǎn)贊或推薦一下)
C:create 新增,U:update 更新,R:retrieve 讀取,D:delete 刪除 ?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/69937.html
摘要:如果隊(duì)列已滿,這個(gè)時(shí)候?qū)懖僮鞯木€程進(jìn)入到寫線程隊(duì)列排隊(duì),等待讀線程將隊(duì)列元素移除騰出空間,然后喚醒寫線程隊(duì)列的第一個(gè)等待線程。數(shù)據(jù)必須從某個(gè)寫線程交給某個(gè)讀線程,而不是寫到某個(gè)隊(duì)列中等待被消費(fèi)。 前言 本文直接參考 Doug Lea 寫的 Java doc 和注釋,這也是我們?cè)趯W(xué)習(xí) java 并發(fā)包時(shí)最好的材料了。希望大家能有所思、有所悟,學(xué)習(xí) Doug Lea 的代碼風(fēng)格,并將其優(yōu)雅...
摘要:線程安全的線程安全的,在讀多寫少的場(chǎng)合性能非常好,遠(yuǎn)遠(yuǎn)好于高效的并發(fā)隊(duì)列,使用鏈表實(shí)現(xiàn)。這樣帶來的好處是在高并發(fā)的情況下,你會(huì)需要一個(gè)全局鎖來保證整個(gè)平衡樹的線程安全。 該文已加入開源項(xiàng)目:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識(shí)的文檔類項(xiàng)目,Star 數(shù)接近 14 k)。地址:https://github.com/Snailclimb... 一 JDK ...
摘要:整個(gè)包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執(zhí)行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據(jù)一系列常見的多線程設(shè)計(jì)模式,設(shè)計(jì)了并發(fā)包,其中包下提供了一系列基礎(chǔ)的鎖工具,用以對(duì)等進(jìn)行補(bǔ)充增強(qiáng)。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發(fā)于一世流云專欄:https...
摘要:在章節(jié)中,我們說過,維護(hù)了一把全局鎖,無論是出隊(duì)還是入隊(duì),都共用這把鎖,這就導(dǎo)致任一時(shí)間點(diǎn)只有一個(gè)線程能夠執(zhí)行。入隊(duì)鎖對(duì)應(yīng)的是條件隊(duì)列,出隊(duì)鎖對(duì)應(yīng)的是條件隊(duì)列,所以每入隊(duì)一個(gè)元素,應(yīng)當(dāng)立即去喚醒可能阻塞的其它入隊(duì)線程。 showImg(https://segmentfault.com/img/bVbgCD9?w=1920&h=1080); 本文首發(fā)于一世流云專欄:https://seg...
摘要:前言這篇主要來講解多線程中一個(gè)非常經(jīng)典的設(shè)計(jì)模式包括它的基礎(chǔ)到拓展希望大家能夠有所收獲生產(chǎn)者消費(fèi)者模式簡(jiǎn)述此設(shè)計(jì)模式中主要分兩類線程生產(chǎn)者線程和消費(fèi)者線程生產(chǎn)者提供數(shù)據(jù)和任務(wù)消費(fèi)者處理數(shù)據(jù)和任務(wù)該模式的核心就是數(shù)據(jù)和任務(wù)的交互點(diǎn)共享內(nèi)存緩 前言 這篇主要來講解多線程中一個(gè)非常經(jīng)典的設(shè)計(jì)模式包括它的基礎(chǔ)到拓展希望大家能夠有所收獲 生產(chǎn)者-消費(fèi)者模式簡(jiǎn)述 此設(shè)計(jì)模式中主要分兩類線程:生產(chǎn)者...
閱讀 1838·2021-11-25 09:43
閱讀 1352·2021-11-22 15:08
閱讀 3763·2021-11-22 09:34
閱讀 3237·2021-09-04 16:40
閱讀 3048·2021-09-04 16:40
閱讀 555·2019-08-30 15:54
閱讀 1345·2019-08-29 17:19
閱讀 1763·2019-08-28 18:13