一、導(dǎo)論
這些天一直在看關(guān)于多線程和高并發(fā)的書籍,也對jdk中的并發(fā)措施了解了些許,看到concurrentHashMap的時(shí)候感覺知識點(diǎn)很亂,有必要寫篇博客整理記錄一下。
當(dāng)資源在多線程下共享時(shí)會產(chǎn)生一些邏輯問題,這個時(shí)候類或者方法會產(chǎn)生不符合正常邏輯的結(jié)果,則不是線程安全的。縱觀jdk的版本更新,可以看到j(luò)dk的開發(fā)人員在高并發(fā)和多線程下了很大的功夫,盡可能的通過jdk原生API來給開發(fā)人員帶來最方便最輕松的高并發(fā)數(shù)據(jù)模型,甚至想完全為開發(fā)人員解決并發(fā)問題,可以看得出來jdk的開發(fā)人員確實(shí)很用心。但是在大量業(yè)務(wù)數(shù)據(jù)的邏輯代碼的情況下高并發(fā)還是不可避免,也不可能完全通過jdk原生的并發(fā)API去解決這些并發(fā)問題,開發(fā)人員不得不自己去空值在高并發(fā)環(huán)境下的數(shù)據(jù)高可用性和一致性。
前面說了jdk原生的API已經(jīng)有了很多的高并發(fā)產(chǎn)品,在java.util.concurrent包下有很多解決高并發(fā),高吞吐量,多線程問題的API。比如線程池ThreadPoolExecutor,線程池工廠Executors,F(xiàn)uture模式下的接口Future,阻塞隊(duì)列BlockingQueue等等。
二、正文
1、數(shù)據(jù)的可見性
直接進(jìn)入正題,concurrentHashMap相信用的人也很多,因?yàn)樵跀?shù)據(jù)安全性上確實(shí)比HashMap好用,在性能上比hashtable也好用。大家都知道線程在操作一個變量的時(shí)候,比如i++,jvm執(zhí)行的時(shí)候需要經(jīng)過兩個內(nèi)存,主內(nèi)存和工作內(nèi)存。那么在線程A對i進(jìn)行加1的時(shí)候,它需要去主內(nèi)存拿到變量值,這個時(shí)候工作內(nèi)存中便有了一個變量數(shù)據(jù)的副本,執(zhí)行完這些之后,再去對變量真正的加1,但是此時(shí)線程B也要操作變量,并且邏輯上也是沒有維護(hù)多線程訪問的限制,則很有可能在線程A在從主內(nèi)存獲取數(shù)據(jù)并在修改的時(shí)候線程B去主內(nèi)存拿數(shù)據(jù),但是這個時(shí)候主內(nèi)存的數(shù)據(jù)還沒有更新,A線程還沒有來得及講加1后的變量回填到主內(nèi)存,這個時(shí)候變量在這兩個線程操作的情況下就會發(fā)生邏輯錯誤。
2、原子性
原子性就是當(dāng)某一個線程A修改i的值的時(shí)候,從取出i到將新的i的值寫給i之間線程B不能對i進(jìn)行任何操作。也就是說保證某個線程對i的操作是原子性的,這樣就可以避免數(shù)據(jù)臟讀。
3、volatile的作用
Volatile保證了數(shù)據(jù)在多線程之間的可見性,每個線程在獲取volatile修飾的變量時(shí)候都回去主內(nèi)存獲取,所以當(dāng)線程A修改了被volatile修飾的數(shù)據(jù)后其他線程看到的一定是修改過后最新的數(shù)據(jù),也是因?yàn)関olatile修飾的變量數(shù)據(jù)每次都要去主內(nèi)存獲取,在性能上會有些犧牲。
4、措施
HashMap在多線程的場景下是不安全的,hashtable雖然是在數(shù)據(jù)表上加鎖,縱然數(shù)據(jù)安全了,但是性能方面確