sleep:Thread的靜態(tài)方法(Thread.sleep())陪蜻,不會(huì)讓出鎖,不需要被喚醒矩屁,睡眠期間過后遍膜,重新獲得cpu資源
wait:Object的非靜態(tài)方法(B.wait()),執(zhí)行后讓出鎖,進(jìn)入等待池扛或,需要被喚醒(notify()和notifyAll())
yield:暫時(shí)讓出CPU資源绵咱,如果有其他線程獲取了CPU資源,就等到下一次執(zhí)行熙兔,否則繼續(xù)執(zhí)行
join:A線程執(zhí)行B線程的join()方法悲伶,執(zhí)行后需要等待B執(zhí)行完才執(zhí)行A
線程安全:
單線程執(zhí)行和多線程執(zhí)行得到的結(jié)果是一樣的
1、不可變
對(duì)象本身不可改變住涉,每次改變都需要新創(chuàng)建一個(gè)對(duì)象麸锉,不需要再采取任何的線程安全保障措施
final關(guān)鍵字修飾;構(gòu)造器私有秆吵;
2淮椰、絕對(duì)線程安全
不需要采取額外的線程安全保障措施
對(duì)象中的方法被synchronized修飾的,也并非絕對(duì)線程安全的對(duì)象纳寂。如Vector的get()主穗、remove()和size()方法,如果其中一個(gè)線程恰好刪除了一個(gè)元素毙芜,另一個(gè)線程恰好在獲取這個(gè)元素時(shí)拋出異常忽媒。此時(shí)需要在線程執(zhí)行的方法中額外加入同步鎖,來保證線程安全腋粥。
3晦雨、相對(duì)線程安全
通常意義上所講的線程安全,如上例子中的Vector隘冲,在大部分情況下是線程安全的闹瞧,但是對(duì)于一些特定順序的連續(xù)調(diào)用,需要做額外的線程安全保障措施展辞,所以我們說Vector就屬于相對(duì)線程安全的類型奥邮。
4、線程兼容
通常意義上所講的線程不安全,對(duì)象本身并不是線程安全的洽腺,但是可以通過正確的同步手段來保障對(duì)象在多線程環(huán)境中可以安全地使用脚粟。
5、線程對(duì)立
排斥多線程的情況蘸朋,無論是否采取同步措施核无,都無法在多線程環(huán)境下使用。
例如Thread類的suspend()方法和resume()方法藕坯,如果兩個(gè)線程同時(shí)持有一個(gè)線程對(duì)象团南,一個(gè)嘗試去中斷線程,一個(gè)嘗試去恢復(fù)線程堕担,并發(fā)進(jìn)行已慢,無論是否同步曲聂,將會(huì)存在死鎖風(fēng)險(xiǎn)
線程安全的實(shí)現(xiàn)方法:
1霹购、互斥同步(阻塞同步)
公平鎖:按一定順序依次獲得鎖
可重入鎖:在公平鎖的基礎(chǔ)上,同一把鎖可以被多次鎖定(例如:按序號(hào)獲得鎖朋腋,此時(shí)有另外一個(gè)擁有相同序號(hào)的線程齐疙,可以不用等待直接獲得鎖)
非公平鎖:在鎖被釋放時(shí),任何一個(gè)等待鎖的線程都有機(jī)會(huì)獲得鎖旭咽,如synchronized
2贞奋、非阻塞同步
CAS指令需要有3個(gè)操作數(shù),分別時(shí)內(nèi)存地址V穷绵,舊的預(yù)期值A(chǔ)轿塔,新值B,當(dāng)且僅當(dāng)V符合A時(shí)仲墨,處理器用B更新A勾缭,否則不執(zhí)行更新
邏輯漏洞(ABA問題):假設(shè)V符合A了,但實(shí)際上A有可能經(jīng)歷了A---B---A的過程
解決方法:版本控制目养,互斥同步(更高效)
3俩由、無同步方案
要保證線程安全,并不是一定就要進(jìn)行同步癌蚁,兩者沒有因果關(guān)系幻梯。
線程本地存儲(chǔ):將需要共享數(shù)據(jù)的代碼保證在一個(gè)線程內(nèi)完成,如消費(fèi)隊(duì)列努释,ThreadLocal(通過threadLocalHashCode(Key)碘梢,找回對(duì)應(yīng)的本地線程變量(Value))
鎖優(yōu)化:
1、自旋鎖與自適應(yīng)自旋
互斥同步對(duì)性能最大的影響是阻塞的實(shí)現(xiàn)伐蒂,掛起線程和恢復(fù)線程的操作都需要轉(zhuǎn)入內(nèi)核態(tài)中完成煞躬,這些操作給系統(tǒng)的并發(fā)性能帶來了很大的影響。
大部分情況下饿自,共享數(shù)據(jù)鎖定狀態(tài)只會(huì)持續(xù)很短時(shí)間汰翠,所以我們可以讓線程不進(jìn)入掛起狀態(tài)龄坪,而是進(jìn)入一個(gè)循環(huán),等待鎖釋放
此時(shí)需要考慮到共享數(shù)據(jù)鎖定狀態(tài)會(huì)持續(xù)很長時(shí)間的情況复唤,自旋鎖等待需要有一定的限制健田,否則只會(huì)白白消耗處理器資源,默認(rèn)自旋次數(shù)10次佛纫,可以通過參數(shù)設(shè)置
自適應(yīng)自旋:自旋時(shí)間不再固定妓局,通過上一次在同一個(gè)鎖上的自旋時(shí)間及擁有者的狀態(tài)來決定,假設(shè)上一次成功了呈宇,虛擬機(jī)會(huì)認(rèn)為這次也有可能成功好爬,進(jìn)而它將允許自旋等待持續(xù)相對(duì)更長的時(shí)間,反之將可能直接省略掉自旋過程甥啄,以避免浪費(fèi)處理器資源
2存炮、鎖消除
虛擬機(jī)及時(shí)編譯器在運(yùn)行時(shí),對(duì)一些代碼上要求同步蜈漓,但是被檢測到不可能存在共享數(shù)據(jù)競爭的鎖進(jìn)行消除
疑問:程序員自己不會(huì)判斷是否需要加鎖穆桂?
答:實(shí)際上大部分看起來不需要加鎖的代碼,都進(jìn)行了同步操作(并不是程序員自己加入的)融虽,例如字符串的連接享完,javac編譯器會(huì)做自動(dòng)優(yōu)化,在JDK1.5前使用StringBuffer進(jìn)行連續(xù)的append(之后是StringBuild)有额,此時(shí)虛擬機(jī)觀察到變量的動(dòng)態(tài)作用域被限制在同一個(gè)方法內(nèi)部般又,其他線程無法訪問到,便會(huì)消除掉這里的鎖
3巍佑、鎖粗化
原則上茴迁,應(yīng)該將同步塊的作用范圍限制得盡量小,這樣做是為了使得需要同步的操作數(shù)量盡可能變小句狼,如果存在鎖競爭笋熬,那等待鎖的線程也能盡快拿到鎖
但是如果一系列的操作對(duì)同一個(gè)對(duì)象反復(fù)加鎖和解鎖,甚至加鎖操作出現(xiàn)在循環(huán)體中腻菇,會(huì)導(dǎo)致不必要的性能損耗
如果虛擬機(jī)探測到有這樣一串零碎的操作都對(duì)同一個(gè)對(duì)象加鎖胳螟,將會(huì)把鎖同步的范圍擴(kuò)展(粗化)到整個(gè)操作序列的外部
4、輕量級(jí)鎖
輕量級(jí)鎖的本意是筹吐,在沒有多線程競爭的前提下糖耸,減少傳統(tǒng)的重量級(jí)鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗(進(jìn)程在用戶態(tài)和內(nèi)核態(tài)之間切換的開銷)
對(duì)象頭(Mark Word)一般包括運(yùn)行時(shí)數(shù)據(jù),hash碼丘薛,GC分代年齡嘉竟,鎖標(biāo)志位,數(shù)組長度等信息
當(dāng)代碼進(jìn)入同步塊時(shí),如果同步對(duì)象沒有被鎖定(鎖標(biāo)志位為"01"狀態(tài))舍扰,虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間倦蚪,用于存儲(chǔ)鎖對(duì)象目前的對(duì)象頭的拷貝,然后虛擬機(jī)嘗試將對(duì)象的對(duì)象頭更新為指向鎖記錄的指針边苹,如果成功了陵且,那么這個(gè)線程就擁有了該對(duì)象的鎖,并且對(duì)象頭的鎖標(biāo)志為更新為“00”狀態(tài)个束,即表示此對(duì)象處于輕量級(jí)鎖定狀態(tài)
如果這個(gè)更新操作失敗了慕购,虛擬機(jī)首先會(huì)檢查對(duì)象頭是否指向當(dāng)前線程的棧幀,如果是則說明當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖茬底,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行沪悲,否則說明這個(gè)鎖對(duì)象已經(jīng)被其他線程搶占了。如果有兩條以上的線程爭用同一個(gè)鎖阱表,那輕量級(jí)鎖就不再有效殿如,要膨脹為重量級(jí)鎖,鎖標(biāo)志的狀態(tài)值變?yōu)椤?0”捶枢,對(duì)象頭中存儲(chǔ)的就是指向重量級(jí)鎖的指針
上面描述的是加鎖過程握截,解鎖也是通過CAS操作來進(jìn)行的,如果對(duì)象頭仍指向著線程的鎖記錄烂叔,那就用CAS操作把對(duì)象當(dāng)前的對(duì)象頭和棧幀中的對(duì)象頭替換回來,如果成功固歪,則整個(gè)同步過程就完成了蒜鸡,否則,說明有其他線程嘗試過獲取該鎖牢裳,那就要在釋放鎖的同時(shí)逢防,喚醒被掛起的線程(為什么要喚醒被掛起的線程,此處我的理解是蒲讯,因?yàn)橐呀?jīng)存在多線程競爭忘朝,需要膨脹為重量級(jí)鎖,所以釋放輕量級(jí)鎖判帮,喚醒被掛起的線程加入重量級(jí)鎖的競爭)
輕量級(jí)鎖使用CAS操作避免了使用互斥量的開銷局嘁,但如果存在鎖競爭,除了互斥量的開銷外晦墙,還額外發(fā)生了CAS操作悦昵,因此在有競爭的情況下,輕量級(jí)鎖會(huì)比傳統(tǒng)重量級(jí)鎖更慢
5晌畅、偏向鎖
偏向鎖的目的是消除數(shù)據(jù)在無競爭情況下的同步原語但指,可以提高帶有同步但無競爭的程序性能
如果程序中大多數(shù)的鎖總是被多個(gè)不同的線程訪問,那偏向鎖就是多余的,有時(shí)候禁用偏向鎖優(yōu)化反而可以提升性能
偏向鎖會(huì)偏向于第一個(gè)獲得它的線程棋凳,如果在接下來的執(zhí)行過程中拦坠,該鎖沒有被其他的線程獲取,則持有偏向鎖的線程將永遠(yuǎn)不需要再進(jìn)行同步
首次加鎖過程與輕量級(jí)鎖基本一樣
當(dāng)有另外一個(gè)線程去嘗試獲取這個(gè)鎖時(shí)剩岳,偏向模式就宣告結(jié)束贪婉,根據(jù)鎖對(duì)象目前是否處于被鎖定的狀態(tài),撤銷偏向后恢復(fù)到未鎖定或輕量級(jí)鎖定狀態(tài)卢肃,后續(xù)就如輕量級(jí)鎖那樣執(zhí)行
參考文獻(xiàn):《深入理解JAVA虛擬機(jī)》