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