加一堆鎖也許能保證線程安全, 但是卻可能造成死鎖
數(shù)據(jù)庫服務(wù)器避免死鎖的方式
- 發(fā)現(xiàn)死鎖;
- 選擇一個(gè)犧牲者, 退出事務(wù);
- 程序繼續(xù)運(yùn)行.
jvm解決死鎖的方式
- 這些線程直接不能用....恢復(fù)的唯一方式是終止并重啟, 然后寄希望于不要再發(fā)生同樣的事.
鎖順序死鎖
如果所有線程通過固定順序獲取多個(gè)鎖, 程序就不會(huì)出現(xiàn)鎖順序死鎖
//警告: 易發(fā)生死鎖
public class LeftRightDeadLock{
private final Object left = new Object();
private final Object right = new Object();
public void leftRight(){
synchronized(left){
sychronized(right){
//do some thing
}
}
}
public void rightLeft(){
synchronized(right){
sychronized(left){
//do some thing
}
}
}
}

LeftRightDeadLock的偶發(fā)時(shí)序
下面的順序死鎖不那么一目了然
//警告:易產(chǎn)生死鎖
public void transferMoney(Account fromAccount,Account toAccount,DollarAmount amount) throws InsufficientFundsException {
synchronized(fromAccount){ //同時(shí)向?qū)Ψ睫D(zhuǎn)賬時(shí), from 和 to兩個(gè)賬戶存在死鎖風(fēng)險(xiǎn)
synchronized(toAccount){
if(fromAccount.getBalance().compareTo(amount)<0)
throw new InsufficientFundsException ();
else{
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}
解決方法
制定鎖的順序 并且整個(gè)應(yīng)用中,獲取鎖必須遵守這個(gè)順序
下面用對(duì)象hashcode決定鎖順序, (hash沖突仍然存在死鎖風(fēng)險(xiǎn), 此時(shí)用競爭機(jī)制保證鎖定)
private static final Object tieLock = new Object();//"加時(shí)賽"鎖, 誰先拿到,誰就先執(zhí)行
public void transferMoney(Account fromAccount,Account toAccount,DollarAmount amount) throws InsufficientFundsException {
class Helper{
public void transfer() throws InsufficientFundsException {
if(fromAccount.getBalance().compareTo(amount)<0)
throw new InsufficientFundsException ();
else{
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
int fromHash = System.identityHashCode(fromAccount)
int toHash = System.identityHashCode(toAccount)
if(fromHash < toHash){//如果from的hashcode小, 則先鎖from
synchronized(fromAccount){
synchronized(toAccount){
new Helper().transfer();
}
}
}else if(fromHash > toHash){//如果to的hashcode小, 則先鎖to對(duì)象
synchronized(toAccount){
synchronized(fromAccount){
new Helper().transfer();
}
}
}else{ //如果兩個(gè)對(duì)象hash相同, 則鎖住整個(gè)轉(zhuǎn)賬對(duì)象. 一次只讓一個(gè)人轉(zhuǎn)
synchronized(tieLock){
synchronized(fromAccount){
synchronized(toAccount){
new Helper().transfer();
}
}
}
}
}
加時(shí)賽鎖有代替方案: 如果賬號(hào)有唯一屬性, 比如賬號(hào)id是唯一的, 那么直接比較賬號(hào)id即可. 這樣可以節(jié)省加時(shí)賽鎖
協(xié)作對(duì)象間的死鎖
//警告: 可能產(chǎn)生死鎖
class Taxi {
@GuardedBy("this)
private Point location,destination;
private final Dispatcher dispatcher;
public Taxi(Dispatcher dispatcher){
this.dispatcher
}
}
總結(jié)一下常見的多線程危險(xiǎn)
- 死鎖
- 饑餓
一個(gè)線程占著資源, 其他線程很久都等不到鎖
- 丟失信號(hào)
????就是在等鎖的時(shí)候,別人放開鎖的消息丟了,,,導(dǎo)致線程一直等下去了...
- 活鎖
????線程沒死, 但是拿不到資源, 就不停的重試,再重試....卡著無法前進(jìn)...解決辦法是做一些隨機(jī), 相互避讓.
java 線程api的優(yōu)先級(jí)僅僅是一種參考, 具體級(jí)別取決于系統(tǒng)的優(yōu)先級(jí).
另外又容易導(dǎo)致某個(gè)線程優(yōu)先級(jí)永遠(yuǎn)高于其他線程而導(dǎo)致饑餓
所以: 盡量不要去改變線程優(yōu)先級(jí)
一旦開始改變線程優(yōu)先級(jí), 則程序的行為就變成平臺(tái)相關(guān)了.并且會(huì)引入饑餓的風(fēng)險(xiǎn)