- Java容器底層原理
- Java高并發(fā)內(nèi)容
- JVM
一. 容器底層原理
- ArrayList
由數(shù)組實(shí)現(xiàn),初始化數(shù)組長(zhǎng)度,每次增加都會(huì)變成之前的1.5倍(擴(kuò)容)。
是線程不安全的,會(huì)產(chǎn)生fail-fast保護(hù)
fail-fast機(jī)制?當(dāng)方法檢測(cè)到對(duì)象的并發(fā)修改,但是不允許這種修改時(shí)就會(huì)拋出ConcurrentModifictionException異常。
2.Vector
由數(shù)組實(shí)現(xiàn),指定擴(kuò)容或者*2
線程安全,也有fail-fast保護(hù),每一個(gè)操作都有鎖保護(hù)
3.LinkedList
基于鏈表實(shí)現(xiàn)
4.hashSet
底層用hashMap實(shí)現(xiàn)。保存的對(duì)象實(shí)際上是底層維護(hù)的Map對(duì)象的key,而value則都是同一個(gè)Object對(duì)象
5.TreeSet
底層是樹(shù),實(shí)現(xiàn)來(lái)自TreeMap。
6.hashMap
底層是數(shù)組和鏈表的組合體-散列,有fail-fast保護(hù)
當(dāng)我們往 HashMap 中 put 元素的時(shí)候,先根據(jù) key 的 hashCode 重新計(jì)算 hash 值,根據(jù) hash 值得到這個(gè)元素在數(shù)組中的位置(即下標(biāo)),如果數(shù)組該位置上已經(jīng)存放有其他元素了,那么在這個(gè)位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數(shù)組該位置上沒(méi)有元素,就直接將該元素放到此數(shù)組中的該位置上
7.TreeMap
底層用紅黑樹(shù)的排序二叉樹(shù)來(lái)保存Map中的每一個(gè)Entry。每一個(gè)Entry都被當(dāng)成“紅黑樹(shù)”的一個(gè)節(jié)點(diǎn)。當(dāng)執(zhí)行put方法時(shí),將該Entry當(dāng)成一個(gè)新節(jié)點(diǎn),添加到已有的紅黑樹(shù)。在插入時(shí),當(dāng)做排序二叉樹(shù)來(lái)操作即可。由于紅黑樹(shù)可能失衡,則進(jìn)行調(diào)整fixAfterInsertion()
調(diào)整涉及到左旋,右旋,著色三個(gè)操作。
8.hashTable
底層是Dictionary,實(shí)現(xiàn)了fail-fast機(jī)制
key和value都不能為null,會(huì)拋出NullPointerException
所有方法幾乎都是synchronized
9.LinedHashMap
相比HashMap,其插入的時(shí)候是有順序的,輸出的時(shí)候可以保持和插入的順序相同。
底層通過(guò)hash表和雙向鏈表來(lái)實(shí)現(xiàn)
10.ConcurrentHashMap
細(xì)分為segment,segment之間允許并發(fā)寫操作。
二. JAVA高并發(fā)內(nèi)容
- 實(shí)現(xiàn)多線程的方式:
Thead類或者Runnable接口
Executor線程池
2.中斷線程的方法:
用stop方法容易導(dǎo)致發(fā)生死鎖
最好使用volatile的布爾類型變量自定義一個(gè)中斷線程的方法
3.TheadLocal
線程內(nèi)部的獨(dú)立變量,可以保證線程安全
注意回收:
TheadLocal.remove()
4.堆和棧?
堆是存放對(duì)象,以及線程之間的共享變量的。
棧是線程自己的內(nèi)存區(qū)域,用于存放本地變量,方法參數(shù)
5.重入鎖和synchronized相比有什么優(yōu)點(diǎn)?
(1)由開(kāi)發(fā)者來(lái)控制加鎖和釋放鎖,比較靈活
(2)可以多次獲取同一把鎖
(3)可以臨時(shí)中斷等待鎖的申請(qǐng)
(4)可以限時(shí)申請(qǐng)鎖,不至于一直等待
(5)公平鎖設(shè)定,可以保證不出現(xiàn)饑餓,先來(lái)先得
6.信號(hào)量semaphore
信號(hào)量申請(qǐng)和釋放的中間臨界區(qū)可以允許多個(gè)線程訪問(wèn)
7.CountDownLatch和CyclicBarrier
CountDownLatch是減計(jì)數(shù)法,減到0后無(wú)法重置。
CyclicBarrier是加計(jì)數(shù),加到指定值后重新從0開(kāi)始(到達(dá)指定值之前是阻塞狀態(tài))
8.線程池:
(1)newFixedThreadPool() 固定大小的線程池
(2)newScheduledThreadPool() 計(jì)劃任務(wù)調(diào)度
(3)newCachedThreadPool() 緩存型
(4)SingleThreadExecutor() 只有一個(gè)線程
使用線程池的好處:
減少在創(chuàng)建和銷毀線程上所花的時(shí)間以及系統(tǒng)資源的開(kāi)銷
如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量線程而導(dǎo)致消耗完系統(tǒng)內(nèi)存
9.鎖的優(yōu)化
(1)減小鎖粒度,如hashMap改成ConcurrentHashMap
(2)使用讀寫鎖代替獨(dú)占鎖
(3)減小鎖的持有時(shí)間
(4)鎖粗化,如果連續(xù)對(duì)同一把鎖請(qǐng)求和釋放的話,整合這些請(qǐng)求可能會(huì)好一些
10.關(guān)于ThreadLocal
多線程使用同一對(duì)象的副本,互不干擾
底層是ThreadLocalMap,Key是ThreadLocal當(dāng)前對(duì)象,Value就是需要保存的值。使用的是弱引用,在線程結(jié)束后,變量就會(huì)被回收。
11.CAS無(wú)鎖算法(原子類的實(shí)現(xiàn)原理-AtomicInteger)
CAS是項(xiàng)樂(lè)觀鎖技術(shù),當(dāng)多個(gè)線程嘗試使用CAS同時(shí)更新同一個(gè)變量時(shí),只有其中一個(gè)線程能更新變量的值,而其它線程都失敗,失敗的線程并不會(huì)被掛起,而是被告知這次競(jìng)爭(zhēng)中失敗,并可以再次嘗試。CAS有3個(gè)操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則什么都不做
12.如何避免死鎖?
(1)采用無(wú)鎖函數(shù)
(2)使用重入鎖的中斷或者限時(shí)等待
13.Fork/Join框架
有些類似于MapReduce的分而治之的思想。Fork就是把任務(wù)分解,Join就是等待前面的執(zhí)行分支執(zhí)行完畢。
14.并發(fā)和并行的區(qū)別
并行是真正意義上的同時(shí)執(zhí)行
而并發(fā)是任務(wù)之間交替執(zhí)行
15.java線程和操作系統(tǒng)線程的關(guān)系?
有相同的線程狀態(tài):創(chuàng)建,就緒,執(zhí)行,阻塞和終止
OS中線程是用戶級(jí)線程和內(nèi)核級(jí)線程
Java的線程是在JVM中實(shí)現(xiàn)的,對(duì)OS是不可見(jiàn)的
16.分布式鎖的實(shí)現(xiàn)
(1)基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖
可以基于鎖表,或者排他鎖(for update語(yǔ)句)
(2)基于緩存實(shí)現(xiàn),性能較高
redis sentx(設(shè)置超時(shí)時(shí)間來(lái)控制鎖釋放)
(3)基于Zookeeper實(shí)現(xiàn),比較可靠
通過(guò)節(jié)點(diǎn)變化監(jiān)聽(tīng)來(lái)通知鎖獲取
沒(méi)有單點(diǎn)問(wèn)題
JVM內(nèi)容
堆內(nèi)區(qū)域:新生代,老年代。新生代是新生的對(duì)象會(huì)被放置的區(qū)域,當(dāng)對(duì)象存活了一定時(shí)間后,會(huì)被移動(dòng)到年老代。新生代GC是復(fù)制算法,GC比較頻繁。老年代一般用標(biāo)記清除。
新生代一般不要設(shè)置過(guò)大,容易導(dǎo)致頻繁GC,甚至如果老年代裝不下年輕代的變量,會(huì)導(dǎo)致頻繁的full GC,十分耗時(shí)