加鎖機制
用鎖來保護狀態
對於可能被多個執行緒同時訪問的可變狀態變數,(包括每個包含多個變數的不變形條件)在訪問它的時候都需要持有同乙個鎖,稱為被這個鎖保護;
if(!vector.containers(element))
活躍性與效能–死鎖和顯示鎖
1.死鎖
定義:當乙個執行緒永遠占有乙個鎖, 而其他執行緒嘗試去獲得這個鎖, 那麼它們將永遠被阻塞。
例如:當執行緒占有鎖l時, 想要獲得鎖m, 但是同時, 執行緒b持有m, 並嘗試獲得l, 兩個執行緒將永遠等待下去, 這種情況是死鎖的最簡單的形式
鎖順序死鎖的原因是:兩個執行緒以不同的順序來獲得相同的鎖。
如果所有執行緒都以固定的順序來獲得鎖,那麼就不會出現鎖順序死鎖的問題。
簡單的鎖順序死鎖:
public class test }}
public void rightleft()}}
}
動態的鎖順序死鎖常發生在鎖的引數傳遞上
例如乙個轉賬操作, 將賬號a的錢轉到賬號b中, 然後在轉賬過程中順序對賬號a和賬號b使用同步鎖, 同時又出現賬號b向賬號a轉賬, 這時應為引數順序導致死鎖:
a:transfermoney(myaccount, youraccount, 10);
b:transfermoney(youraccount, myaccount, 10);
如果執行的時機不對,可能發生a持有myaccount,等待youraccount,b持有youraccount,等待myaccount,這樣導致死鎖。
public void transfermoney(account fromaccount, account toaccount, dollaramount amount) else
} }
}
動態的鎖順序死鎖還是由於獲取鎖的順序不同導致的,為了解決這個問題,必須定義鎖的順序,並且整個程式都必須按照這個順序來獲取鎖:
public void transfermoney(final account fromaccount, final account toaccount, final dollaramount amount) else
} }
// 通過唯一hashcode來統一鎖的順序, 如果account具有唯一鍵, 可以採用該鍵來作為順序.
int fromhash = system.identityhashcode(fromaccount);
int tohash = system.identityhashcode(toaccount);
if (fromhash < tohash)
} } else if (fromhash > tohash)
} } else
} }
} }
class taxi
public synchronized string getlocation()
public synchronized void setlocation()
}class dispather
public synchronized void notifytaxi()
public synchronized void getinfo()
}
**看起來沒有明顯地鎖順序問題,taxi的setlocation()本身已經加了內建鎖,然後呼叫dispather.notifytaxi(),notifytaxi()也加了鎖,所以這個方法實際獲取了兩個鎖,同理dispather。因此也存在獲取鎖的順序導致死鎖的問題。
有界線程池/資源池與相互依賴的任務不能一起使用
如果要獲取多個鎖,必須考慮鎖的順序,盡可能使用開放呼叫
使用顯示鎖檢測死鎖,並且可以從死鎖中恢復過來
通過執行緒轉儲資訊來分析死鎖
效能和可伸縮性
應用程式的效能可以用多個指標來衡量:服務時間,延遲時間,吞吐率,效率,可伸縮性和容量。
可伸縮性:當增加計算機資源時(記憶體,cpu,儲存容量或io頻寬),程式的吞吐量或者處理能力要相應得提高(因此序列的可伸縮性是很差的)。
1. 上下文切換
如果可執行的執行緒數大於cpu屬相,那麼作業系統會將某個正在執行的執行緒排程出來,儲存當前執行緒執行的上下文(以便下次再次排程能儲存進度),排程其他執行緒使用cpu,並將新排程的執行緒的上下文設定為當前的上下文。
2. 記憶體同步
在synchronized和volatile提供的可見性保證中,可能會使用一些特殊指令——記憶體柵欄(barrier)。記憶體柵欄可以重新整理快取,使快取無效,這可能會對效能帶來影響,因為它將抑制一些編譯器優化。在記憶體柵欄中,大多數操作是不能重排序的。
現代的jvm能通過優化去掉一下不會發生競爭的鎖,以減少不必要的效能開銷。如果乙個鎖物件只能由當前執行緒訪問(通常是區域性變數),那麼jvm會去掉這個鎖的獲取
3.阻塞
當在鎖發生競爭時,失敗的執行緒肯定會被阻塞。當執行緒無法獲取某個鎖或在某個條件等待或在io操作阻塞時,需要被掛起,這個過程將包括兩次額外的上下文切換,以及必要的作業系統和快取操作。
有兩個因素將影響在鎖上發生競爭的可能性:鎖的請求頻率,以及每次持有鎖的時間。
三種方式降低鎖的競爭程度:
private final setusers = new hashset();
private final setquerys = new hashset();
public synchronized void adduser(string user)
public synchronized void addquery(string query)
對於上面這個**,adduser和addquery是對兩個不同物件的操作,如果為兩個操作都加上同乙個鎖,adduser和addquery不能同時併發,但更好的方式是應該讓adduser和addquery可以同時執行,因為它們不影響彼此。
private final setusers = new hashset();
private final setquerys = new hashset();
public void adduser(string user)
}public void addquery(string query)
}
Java併發程式設計一
不要用run 來開啟執行緒,它只會在當前執行緒中,序列執行run 方法中的 建立執行緒時,推薦傳入runnable介面的例項,因為預設的thread.run 就是直接呼叫內部的runnable介面,這樣避免了過載thread.run 因此使用runnable介面來告訴執行緒該做什麼更為合理。publ...
Java 併發程式設計 基礎 一
同步 和 非同步描述方法的呼叫。同步 方法呼叫一旦開始,呼叫者必須等到方法呼叫返回後,才能繼續後續的行為。序列的進行方法的呼叫。非同步方法呼叫更像乙個訊息傳遞,一旦方法呼叫就會立即返回。併發 多個任務交替執行。並行 多個cpu的系統上,多個cpu同時執行任務。用來表示公共資源或者公共資料。可以被多個...
學習java併發程式設計(一)
1 任務的認識 任務可以看作是乙個implements runnable的乙個類,任務的內容就是定義在run 方法中的內容 2 thread executor 當使用new thread runnable r start 時,即客戶端 也就是編寫程式的開發人員 直接在新執行緒中執行任務,而在使用ex...