集合
java中的集合一般分為List蚣常、Map继榆、Set巾表、Queue汁掠。
List
列表集合
- ArrayList:最常用的List略吨,底層為Object數(shù)組集币,繼承了
RandomAccess, Cloneable, java.io.Serializable
等接口。
繼承RandomAccess表示該類支持快速隨機訪問翠忠,盡量用for(int i = 0; i < size; i++) 來遍歷鞠苟。反之使用Iterator迭代器。
繼承Cloneable表示該類支持淺拷貝秽之。
繼承java.io.Serializable表示該類支持序列化
- LinkedList:底層實現(xiàn)為雙向鏈表当娱。不支持隨機訪問。實現(xiàn)Queue接口考榨,支持隊列操作跨细。
- Vector:線程安全的ArrayList,所有方法全部使用synchronized標(biāo)記河质。雖然方法都是線程安全的冀惭,但是如果使用Iterator迭代器進(jìn)行遍歷的話,還是線程不安全的掀鹅。但是使用foreach()方法進(jìn)行遍歷是線程安全的散休。
- CopyOnWriteArrayList:底層實現(xiàn)為Object數(shù)組,方法是線程不安全的乐尊,但是遍歷是線程安全的戚丸。通過拷貝List實現(xiàn)遍歷安全。
Map
鍵值對集合
- 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時鏈表化络拌。
- HashTable:線程不安全,key和value不允許null回溺,內(nèi)部使用Entry數(shù)組保存數(shù)據(jù)春贸。不常用混萝,為什么不用HashMap。
- SynchronizedMap:線程安全萍恕,由synchronizedMap創(chuàng)建逸嘀,實現(xiàn)線程安全的方式是通過synchronized調(diào)用相關(guān)方法。并發(fā)性能差允粤。
- ConcurrentHashMap:線程安全的HashMap崭倘,實現(xiàn)方式為CAS操作(jdk1.8之前為分段鎖)。并發(fā)性能好类垫。
Set
不重復(fù)的集合
- HashSet:最常用的Set司光,一般用來過濾數(shù)據(jù)、保存配置悉患,底層使用HashMap的key保存數(shù)據(jù)(實現(xiàn)數(shù)據(jù)不重復(fù))残家。
- TreeSet:可以按某個順序保存數(shù)據(jù),通過構(gòu)造方法傳入Comparator實現(xiàn)售躁。
Queue
隊列
- LinkedList:上面有
- BlockingQueue:阻塞隊列坞淮,多線程中實現(xiàn)線程的通信,經(jīng)常使用迂求。常用LinkedBlockingQueue碾盐,通過分離讀寫鎖實現(xiàn)并發(fā)。還有ArrayBlockingQueue揩局,單鎖實現(xiàn)并發(fā)毫玖。
多線程
synchronizd
java中,每個對象都自帶監(jiān)視器凌盯。synchronizd有三種使用方式:
-
synchronizd(object){...}
付枫,對任意代碼塊加鎖。線程會嘗試獲取object的監(jiān)視器驰怎,成功獲取的線程進(jìn)入臨界區(qū)阐滩。其他線程阻塞。直到臨界區(qū)代碼執(zhí)行完畢县忌,釋放object的監(jiān)視器掂榔,其他線程再次進(jìn)行競爭。保證了臨界區(qū)一次只有一個線程進(jìn)入症杏。 - synchronizd修飾普通方法装获,線程會嘗試獲取該方法對象的監(jiān)視器。如果兩個不同對象調(diào)用同一個synchronizd修飾的方法厉颤,無法保證線程執(zhí)行的唯一性穴豫,synchronizd不會發(fā)揮作用。
- 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修肠。
- ReentrantLock:可重入鎖,最普通的Lock實現(xiàn)類户盯。
- 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í)行拒絕策略脸甘。
多線程通信
- wait和notify:最基礎(chǔ)的線程通信,wait使線程等待偏灿,notify喚醒等待線程丹诀。通過對象實現(xiàn),類似
synchronizd(object)
。 - CountDownLatch:倒計數(shù)器铆遭,await檢查計數(shù)器是否為0扁藕,countDown使計數(shù)減1。
- CyclicBarrier:實現(xiàn)線程間的相互等待疚脐,當(dāng)所有線程調(diào)用await后亿柑,所有線程開始向下執(zhí)行。
- Callable:有返回值的Runnable棍弄,可以使用FutureTask接收返回值望薄,會阻塞。如果使用線程池呼畸,則不會阻塞痕支。
- BlockingQueue:阻塞隊列。生產(chǎn)者-消費者模型中經(jīng)常使用蛮原。