java——集合、多線程

集合

java中的集合一般分為List蚣常、Map继榆、Set巾表、Queue汁掠。

List

列表集合

  1. ArrayList:最常用的List略吨,底層為Object數(shù)組集币,繼承了RandomAccess, Cloneable, java.io.Serializable等接口。

繼承RandomAccess表示該類支持快速隨機訪問翠忠,盡量用for(int i = 0; i < size; i++) 來遍歷鞠苟。反之使用Iterator迭代器。
繼承Cloneable表示該類支持淺拷貝秽之。
繼承java.io.Serializable表示該類支持序列化

  1. LinkedList:底層實現(xiàn)為雙向鏈表当娱。不支持隨機訪問。實現(xiàn)Queue接口考榨,支持隊列操作跨细。
  2. Vector:線程安全的ArrayList,所有方法全部使用synchronized標(biāo)記河质。雖然方法都是線程安全的冀惭,但是如果使用Iterator迭代器進(jìn)行遍歷的話,還是線程不安全的掀鹅。但是使用foreach()方法進(jìn)行遍歷是線程安全的散休。
  3. CopyOnWriteArrayList:底層實現(xiàn)為Object數(shù)組,方法是線程不安全的乐尊,但是遍歷是線程安全的戚丸。通過拷貝List實現(xiàn)遍歷安全。

Map

鍵值對集合

  1. HashMap:最常用的Map扔嵌,線程不安全限府,允許key和value為null,內(nèi)部使用Node數(shù)組保存k-v數(shù)據(jù)痢缎。默認(rèn)大小為16胁勺,調(diào)節(jié)因子為0.75(表示當(dāng)元素達(dá)到數(shù)組大小的0.75時,數(shù)據(jù)進(jìn)行擴容牺弄,擴大兩倍)姻几。通過計算key的hash值來確定key-value的位置。如果出現(xiàn)位置沖突势告,jdk1.8前是形成鏈表蛇捌,jdk1.8及以后引入鏈表+紅黑樹,默認(rèn)鏈表元素大于8時紅黑樹化咱台,樹元素小于6時鏈表化络拌。
  2. HashTable:線程不安全,key和value不允許null回溺,內(nèi)部使用Entry數(shù)組保存數(shù)據(jù)春贸。不常用混萝,為什么不用HashMap。
  3. SynchronizedMap:線程安全萍恕,由synchronizedMap創(chuàng)建逸嘀,實現(xiàn)線程安全的方式是通過synchronized調(diào)用相關(guān)方法。并發(fā)性能差允粤。
  4. ConcurrentHashMap:線程安全的HashMap崭倘,實現(xiàn)方式為CAS操作(jdk1.8之前為分段鎖)。并發(fā)性能好类垫。

Set

不重復(fù)的集合

  1. HashSet:最常用的Set司光,一般用來過濾數(shù)據(jù)、保存配置悉患,底層使用HashMap的key保存數(shù)據(jù)(實現(xiàn)數(shù)據(jù)不重復(fù))残家。
  2. TreeSet:可以按某個順序保存數(shù)據(jù),通過構(gòu)造方法傳入Comparator實現(xiàn)售躁。

Queue

隊列

  1. LinkedList:上面有
  2. BlockingQueue:阻塞隊列坞淮,多線程中實現(xiàn)線程的通信,經(jīng)常使用迂求。常用LinkedBlockingQueue碾盐,通過分離讀寫鎖實現(xiàn)并發(fā)。還有ArrayBlockingQueue揩局,單鎖實現(xiàn)并發(fā)毫玖。

多線程

synchronizd

java中,每個對象都自帶監(jiān)視器凌盯。synchronizd有三種使用方式:

  1. synchronizd(object){...}付枫,對任意代碼塊加鎖。線程會嘗試獲取object的監(jiān)視器驰怎,成功獲取的線程進(jìn)入臨界區(qū)阐滩。其他線程阻塞。直到臨界區(qū)代碼執(zhí)行完畢县忌,釋放object的監(jiān)視器掂榔,其他線程再次進(jìn)行競爭。保證了臨界區(qū)一次只有一個線程進(jìn)入症杏。
  2. synchronizd修飾普通方法装获,線程會嘗試獲取該方法對象的監(jiān)視器。如果兩個不同對象調(diào)用同一個synchronizd修飾的方法厉颤,無法保證線程執(zhí)行的唯一性穴豫,synchronizd不會發(fā)揮作用。
  3. synchronizd修飾靜態(tài)方法逼友,線程會嘗試獲取該方法類的監(jiān)視器精肃。Class對象的唯一性會保證synchronizd發(fā)揮作用秤涩。
    如果1中的object為Class對象,那么效果與3相同司抱。
    synchronizd為可重入鎖筐眷。可以在獲取對象的監(jiān)視器后状植,再次調(diào)用同對象的synchronizd浊竟。

volatile

volatile可以保證所有線程看到的變量值都是最新的怨喘。任何線程修改了volatile變量津畸,會立刻反映到其他線程中。

如果變量不用volatile修飾必怜,那么線程對變量的修改不會立刻反映到其他線程中肉拓,因為有本地緩存。如果不用volatile梳庆,可以用synchronizd(Lock)實現(xiàn)變量的刷新暖途。離開synchronizd代碼塊時,所有的修改會強制刷新入主內(nèi)存膏执。
volatile保證可見性驻售,不保證原子性。如果有對volatile變量的原子性操作更米,需要加鎖欺栗。
volatile可以防止 JVM 進(jìn)行指令重排優(yōu)化。
禁止重排序的規(guī)則為:第一個操作為volatile讀征峦;第二個操作為volatile寫迟几;第一個操作為volatile寫,第二個操作為volatile讀栏笆。
禁止重排序可以防止因為重排序在多線程環(huán)境下引起的順序一致性錯誤类腮。

private static Map<String,String> value ;
private static volatile boolean flag = fasle ;
//以下方法發(fā)生在線程 A 中 初始化 Map
public void initMap(){
   //耗時操作
   value = getMapValue() ;//1
   flag = true ;//2
}
//發(fā)生在線程 B中 等到 Map 初始化成功進(jìn)行其他操作
public void doSomeThing(){
   while(!flag){
       sleep() ;
   }
   //dosomething
   doSomeThing(value);
}

如果flag不用volatile修飾,那么線程A有可能會先執(zhí)行2再執(zhí)行1蛉加,這樣會造成錯誤蚜枢。同時volatile修飾的變量可以保證線程A改變flag的值之后,線程B立刻可見针饥。否則容易因為緩存導(dǎo)致死循環(huán)厂抽。

Lock

鎖接口,主要實現(xiàn)類為ReentrantLock打厘、ReadLock和WriteLock修肠。

  1. ReentrantLock:可重入鎖,最普通的Lock實現(xiàn)類户盯。
  2. ReentrantReadWriteLock:讀寫分離鎖嵌施,繼承自ReadWriteLock饲化,內(nèi)部實現(xiàn)了ReadLock和WriteLock,讀鎖和寫鎖吗伤。通過分離讀寫提高并發(fā)性吃靠。允許同時多個讀線程,只允許同時單個寫線程(此時不允許讀)足淆。適用于讀多寫少巢块。
  • void lock():加鎖操作
  • void lockInterruptibly() throws InterruptedException:可以拋出中斷異常的加鎖
  • boolean tryLock():嘗試獲取鎖,返回結(jié)果
  • boolean tryLock(long time, TimeUnit unit) throws InterruptedException:嘗試在指定的時間內(nèi)獲取鎖巧号,可以被中斷族奢。
  • void unlock():解鎖操作,這個操作是必須的丹鸿,一般放在finally中執(zhí)行越走。

如果想用Lock進(jìn)行同步操作,注意多個線程中操作同一個Lock靠欢。

ThreadPoolExecutor

ThreadPoolExecutor線程池廊敌,可以自定義線程池的所有參數(shù)。

推薦通過new ThreadPoolExecutor()創(chuàng)建線程池

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSize:線程池核心線程门怪,線程池啟動和平時運行的線程數(shù)骡澈。
  • maximumPoolSize:線程池最大線程。當(dāng)workQueue中放滿之后掷空,會啟動額外的線程肋殴,最大為maximumPoolSize。
  • keepAliveTime:當(dāng)線程池中線程的數(shù)量超過corePoolSize時拣帽,閑置線程會在keepAliveTime后被回收疼电,直到線程數(shù)變?yōu)閏orePoolSize。
  • unit:keepAliveTime的時間單位
  • workQueue:任務(wù)阻塞隊列减拭,當(dāng)任務(wù)無法被線程池中的線程接手處理時蔽豺,會暫時存放在workQueue阻塞隊列中。直到線程池處理完當(dāng)前任務(wù)拧粪,然后從workQueue中獲取任務(wù)進(jìn)行處理修陡。
  • threadFactory:線程工廠,在新線程創(chuàng)建時對其進(jìn)行初始化可霎。
  • handler:拒絕策略魄鸦。當(dāng)workQueue已滿,線程數(shù)達(dá)到maximumPoolSize癣朗,線程池會執(zhí)行拒絕策略拾因。

線程池規(guī)則

線程池的線程執(zhí)行規(guī)則跟任務(wù)隊列有很大的關(guān)系。

下面都假設(shè)任務(wù)隊列沒有大小限制:

  • 如果線程數(shù)量 <= 核心線程數(shù)量,那么直接啟動一個核心線程來執(zhí)行任務(wù)绢记,不會放入隊列中扁达。
  • 如果線程數(shù)量 > 核心線程數(shù),但 <= 最大線程數(shù)蠢熄,并且任務(wù)隊列是LinkedBlockingDeque的時候跪解,超過核心線程數(shù)量的任務(wù)會放在任務(wù)隊列中排隊。
  • 如果線程數(shù)量 > 核心線程數(shù)签孔,但 <= 最大線程數(shù)叉讥,并且任務(wù)隊列是SynchronousQueue的時候,線程池會創(chuàng)建新線程執(zhí)行任務(wù)饥追,這些任務(wù)也不會被放在任務(wù)隊列中图仓。這些線程屬于非核心線程,在任務(wù)完成后判耕,閑置時間達(dá)到了超時時間就會被清除透绩。
  • 如果線程數(shù)量 > 核心線程數(shù),并且 > 最大線程數(shù)壁熄,當(dāng)任務(wù)隊列是LinkedBlockingDeque,會將超過核心線程的任務(wù)放在任務(wù)隊列中排隊碳竟。也就是當(dāng)任務(wù)隊列是LinkedBlockingDeque并且沒有大小限制時草丧,線程池的最大線程數(shù)設(shè)置是無效的,他的線程數(shù)最多不會超過核心線程數(shù)莹桅。
  • 如果線程數(shù)量 > 核心線程數(shù)昌执,并且 > 最大線程數(shù),當(dāng)任務(wù)隊列是SynchronousQueue的時候诈泼,線程池會執(zhí)行拒絕策略懂拾。

任務(wù)隊列大小有限時:

  • 當(dāng)LinkedBlockingDeque塞滿時,新增的任務(wù)會直接創(chuàng)建新線程來執(zhí)行铐达,當(dāng)創(chuàng)建的線程數(shù)量超過最大線程數(shù)量時岖赋,線程池會執(zhí)行拒絕策略。
  • SynchronousQueue沒有數(shù)量限制瓮孙。因為他根本不保持這些任務(wù)唐断,而是直接交給線程池去執(zhí)行。當(dāng)任務(wù)數(shù)量超過最大線程數(shù)時杭抠。線程池會執(zhí)行拒絕策略脸甘。

多線程通信

  1. wait和notify:最基礎(chǔ)的線程通信,wait使線程等待偏灿,notify喚醒等待線程丹诀。通過對象實現(xiàn),類似synchronizd(object)
  2. CountDownLatch:倒計數(shù)器铆遭,await檢查計數(shù)器是否為0扁藕,countDown使計數(shù)減1。
  3. CyclicBarrier:實現(xiàn)線程間的相互等待疚脐,當(dāng)所有線程調(diào)用await后亿柑,所有線程開始向下執(zhí)行。
  4. Callable:有返回值的Runnable棍弄,可以使用FutureTask接收返回值望薄,會阻塞。如果使用線程池呼畸,則不會阻塞痕支。
  5. BlockingQueue:阻塞隊列。生產(chǎn)者-消費者模型中經(jīng)常使用蛮原。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卧须,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子儒陨,更是在濱河造成了極大的恐慌花嘶,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹦漠,死亡現(xiàn)場離奇詭異椭员,居然都是意外死亡,警方通過查閱死者的電腦和手機笛园,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門隘击,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人研铆,你說我怎么就攤上這事埋同。” “怎么了棵红?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵凶赁,是天一觀的道長。 經(jīng)常有香客問我窄赋,道長哟冬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任忆绰,我火速辦了婚禮浩峡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘错敢。我一直安慰自己翰灾,他們只是感情好缕粹,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纸淮,像睡著了一般平斩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咽块,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天绘面,我揣著相機與錄音,去河邊找鬼侈沪。 笑死揭璃,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亭罪。 我是一名探鬼主播瘦馍,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼应役!你這毒婦竟也來了情组?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤箩祥,失蹤者是張志新(化名)和其女友劉穎院崇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滥比,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡亚脆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盲泛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡键耕,死狀恐怖寺滚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屈雄,我是刑警寧澤村视,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站酒奶,受9級特大地震影響蚁孔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惋嚎,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一杠氢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧另伍,春花似錦鼻百、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽因悲。三九已至,卻和暖如春勺爱,著一層夾襖步出監(jiān)牢的瞬間晃琳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工琐鲁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卫旱,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓绣否,卻偏偏與公主長得像誊涯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蒜撮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

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