java線程安全

一呛讲、定義

線程安全指的是多個線程并發(fā)訪問共享資源時禾怠,不會出現(xiàn)數(shù)據(jù)不一致或其他意外情況的情況。

二贝搁、如何實現(xiàn)線程安全

不可變對象

不可變對象是指一旦創(chuàng)建吗氏,其狀態(tài)不能被修改的對象。
因為不可變對象的狀態(tài)不可變雷逆,所以它們可以安全地在多個線程之間共享弦讽。

ThreadLocal

ThreadLocal是一種特殊的變量,它為每個線程提供了一個獨立的副本膀哲。

volatile

只能用于修飾變量往产。

優(yōu)點:變量對于所有線程的可見性、禁止指令重排某宪。
缺點:不能保證原子性仿村、頻繁讀寫volatile變量的開銷大。

同步(synchronized)

通過synchronized 關鍵字給方法或代碼塊加上內(nèi)置鎖來實現(xiàn)線程安全

優(yōu)點:簡單易用兴喂、支持可重用鎖蔼囊。
缺點:性能問題包颁、只能保護代碼塊或方法。

Lock

使用Lock鎖機制可以實現(xiàn)更加靈活的鎖操作压真,比synchronized關鍵字更加高效。

優(yōu)點:可以重復獲取鎖避免死鎖蘑险,性能好滴肿,可擴展(公平鎖非公平鎖、可重入讀寫鎖等)
缺點:代碼復雜

原子操作(CAS)

原子操作是一種無需加鎖的線程安全操作方式佃迄,可以保證多個線程同時訪問同一個變量時泼差,仍能保證數(shù)據(jù)的一致性。Java中通過使用原子操作類中的原子操作方法呵俏,實現(xiàn)線程安全堆缘。

三、使用線程安全類

synchronized

Vector普碎,Stack吼肥,StringBuffer,HashTable麻车,Timer,TimerTask

Java提供了java.util.concurrent.*

CopyOnWriteArrayList:線程安全的ArrayList,將原有的數(shù)組復制一份斤寇,然后在新數(shù)組上進行修改操作棉饶,最后再賦給原的數(shù)組引用。相對Vector更加高效赁咙。
ConcurrentHashMap等

還提供了更多的線程安全類和工具钮莲,
如Semaphore、CountDownLatch彼水、CyclicBarrier崔拥、ReadWriteLock等

Java提供了java.util.concurrent.atomic包

包括AtomicInteger、AtomicLong猿涨、AtomicBoolean在內(nèi)7種握童。

Java提供的工具類Collections.synchronized*

用于將一個非線程安全的集合包裝成一個線程安全的集合。
它通過對集合的操作加上同步鎖(使用synchronized關鍵字)的方式來實現(xiàn)線程安全叛赚。

Map<String, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
image.png

四澡绩、拓展

StringBuilder 和 StringBuffer

特性對比:
  • String : 不可變字符序列,效率低俺附,但是復用率高肥卡。
  • StringBuffer : 可變字符序列,效率較高事镣,且線程安全步鉴。
  • StringBuilder : 可變字符序列,效率最高,但線程不安全氛琢。
使用場景對比:
  • String : 適用于字符串很少被修改喊递,且被多個對象引用的情況,比如定義數(shù)據(jù)庫的IP信息阳似,配置信息等骚勘。
  • StringBuffer : 適用于存在大量修改字符串的情況,且滿足 多線程條件撮奏。
  • StringBuilder : 適用于存在大量修改字符串的情況俏讹,且滿足 單線程條件
import org.springframework.util.StopWatch;

public class StringTest {
    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch("StringTest");
        stopWatch.start("String");
        String s = "";
        for (int i = 0; i < 100000; i++) {
            s += i;
        }
        stopWatch.stop();
        stopWatch.start("StringBuilder");
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 500000; i++) {
            stringBuilder.append(i);
        }
        stopWatch.stop();
        stopWatch.start("StringBuffer");
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < 500000; i++) {
            stringBuffer.append(i);
        }
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }
}

/*
StopWatch 'StringTest': running time (millis) = 2521
-----------------------------------------
ms     %     Task name
-----------------------------------------
02503  099%  String
00007  000%  StringBuilder
00011  000%  StringBuffer
*/

HashMap畜吊、HashTable泽疆、ConcurrentHashMap區(qū)別

HashMap

線程不安全,可以存儲null鍵和null值

Hashtable

Hashtable是線程安全的,但鎖的是整個Hashtable對象玲献,無論key還是value都不能為null

缺點:
(1) 多個線程訪問同一個Hashtable對象時殉疼,無論做什么操作,都會產(chǎn)生鎖沖突捌年;
(2) 如果某個線程觸發(fā)了擴容機制株依,那么就會由這個線程完成整個擴容過程,如果元素過多延窜,效率則非常低恋腕,其它線程阻塞等待的時間也會更長。
Java官方已經(jīng)不推薦使用Hashtable了

ConcurrentHashMap

ConcurrentHashMap相較于Hashtable做了許多的優(yōu)化逆瑞,核心思路就是降低鎖沖突概率~

1荠藤、鎖粒度的控制ConcurrentHashMap不是鎖整個對象,而是使用多把鎖获高,對每個鏈表(哈希桶)都進行加鎖哈肖,只有當兩個線程同時訪問同一個鏈表時,才會產(chǎn)生鎖競爭念秧,因此極大地降低了鎖沖突的概率淤井。

2、讀操作不加鎖
ConcurrentHashMap只對寫操作進行加鎖摊趾,讀操作沒有加鎖币狠,此時會有三種情況:

(1) 兩個線程同時修改一個哈希桶時才會產(chǎn)生鎖沖突;

(2) 兩個線程同時讀數(shù)據(jù)砾层,不會有鎖沖突漩绵;

(3) 一個線程修改,一個線程讀肛炮,也沒有鎖沖突止吐。

第三種情況可能會有線程不安全問題宝踪,這和我們寫的代碼有關,但是ConcurrentHashMap中的讀操作使用了Volatile碍扔,來保證讀到的數(shù)據(jù)不是修改了一半的數(shù)據(jù)瘩燥。

3、利用了CAS的特性
ConcurrentHashMap充分利用了CAS的特性不同,避免出現(xiàn)重量級鎖的情況颤芬。

比如維護元素個數(shù)(size)時,就是通過CAS來更新~

4套鹅、優(yōu)化擴容操作
Hashtable的擴容機制是,創(chuàng)建一個更大的新數(shù)組汰具,然后由一個線程一次性把舊數(shù)組中的元素搬運到新數(shù)組卓鹿。

而ConcurrentHashMap則是讓新數(shù)組和舊數(shù)組同時存在一段時間,在這期間留荔,后續(xù)每個操作ConcurrentHashMap的線程都會負責搬運舊數(shù)組的一部分元素到新數(shù)組吟孙,直到搬運完舊數(shù)組的最后一個元素時,再把舊數(shù)組刪除~

搬運期間聚蝶,插入元素時直接插入到新數(shù)組中杰妓;查詢元素時,新數(shù)組和舊數(shù)組一起查碘勉。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巷挥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子验靡,更是在濱河造成了極大的恐慌倍宾,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胜嗓,死亡現(xiàn)場離奇詭異高职,居然都是意外死亡,警方通過查閱死者的電腦和手機辞州,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門怔锌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人变过,你說我怎么就攤上這事埃元。” “怎么了媚狰?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵亚情,是天一觀的道長。 經(jīng)常有香客問我哈雏,道長楞件,這世上最難降的妖魔是什么衫生? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮土浸,結果婚禮上罪针,老公的妹妹穿的比我還像新娘。我一直安慰自己黄伊,他們只是感情好泪酱,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著还最,像睡著了一般墓阀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拓轻,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天斯撮,我揣著相機與錄音,去河邊找鬼扶叉。 笑死勿锅,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的枣氧。 我是一名探鬼主播溢十,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼达吞!你這毒婦竟也來了张弛?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤酪劫,失蹤者是張志新(化名)和其女友劉穎乌庶,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體契耿,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡瞒大,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了搪桂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片透敌。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖踢械,靈堂內(nèi)的尸體忽然破棺而出酗电,到底是詐尸還是另有隱情,我是刑警寧澤内列,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布撵术,位于F島的核電站,受9級特大地震影響话瞧,放射性物質(zhì)發(fā)生泄漏嫩与。R本人自食惡果不足惜寝姿,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望划滋。 院中可真熱鬧饵筑,春花似錦、人聲如沸处坪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽同窘。三九已至玄帕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間想邦,已是汗流浹背裤纹。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留案狠,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓钱雷,卻偏偏與公主長得像骂铁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子罩抗,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

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