java synchronized到底鎖住的是什麼
剛學java的時候,只知道synchronized一個執行緒鎖,能夠鎖住程式碼,但是它真的能像我想的那樣,能夠鎖住程式碼嗎?
在討論之前先看一下專案中常見關於synchronized的用法:
public synchronized void syncCurrentObject() {
System.out.println(Thread.currentThread().getName()+"..start.."+"-----"+System.currentTimeMillis());
try {
Thread.sleep (1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"..end.."+"-----"+System.currentTimeMillis());
}
這樣能否在多個執行緒訪問時候,保證只有一個執行緒進入方法,其它執行緒阻塞嗎?
我用執行緒池建立三個執行緒容量,分別啟動五個執行緒:
public static void syncCurrentObjectTest() {
ExecutorService exec = Executors.newFixedThreadPool(3);
// final GenerateCode gCode = new GenerateCode();
for (int i = 0; i < 5; i++) {
exec.execute(new Runnable() {
@Override
public void run() {
GenerateCode gCode = new GenerateCode();
gCode.syncCurrentObject();
}
});
}
exec.shutdown();
}
執行效果截圖:
根據截圖的輸入日誌說明將synchronized加在方法上並不能讓執行緒安全,而是多個執行緒並行執行。比如:執行緒3並沒有等執行緒1執行完成後再執行,而是執行緒1休眠的時候,執行緒3直接獲得鎖,進行執行。那麼在原有的實現上,如果保證執行緒安全呢?
解決思路:在多個執行緒呼叫synchronized修飾的方法時,呼叫synchronized方法是同一個物件。
具體解決方案是:將GenerateCode 物件建立一次(寫成單例更好),然後呼叫synchronized修飾方法。
具體修改截圖如下:
為什麼這樣改就可以呢?原理是因為對於成員方法,synchronized只能鎖住當前物件的執行緒,其它物件的執行緒無法鎖住。而且synchronized放在方法和在方法內synchronize(this)是等價的。都只能鎖住當前物件。
但是如果想鎖住不同物件的多個執行緒,該怎麼做呢?示例程式碼如下:
//直接在靜態方法上加synchronized 執行緒安全
public synchronized static void syncStatic() {
//dosomething..
}
//在靜態方法上synchronized當前類 執行緒安全
public static void syncCurrentClass() {
synchronized(GenerateCode.class){
//dosomething..
}
}
//在成員方法上synchronized當前類 執行緒安全
public void syncCurrentObjectByThisClass() {
synchronized(GenerateCode.class){
//dosomething..
}
}
用synchronized鎖住當前類位元組碼,當前類中總是隻有一個執行緒可以進入執行,其它執行緒進入阻塞。
總結:synchronized可以鎖當前物件,也可以鎖類。
synchronized鎖住當前物件的寫法:
public synchronized void a(){
}
public void ab(){
synchronized (this){
}
}
synchronized鎖住當前類的寫法:
public synchronized static void a(){
}
public static void a(){
synchronized (類名){
}
}
public void ab(){
synchronized (類名){
}
}
我的理解是:當synchronized作用在物件時候,同一個物件中的執行緒是互斥的,只有一個執行緒執行完成後,另外一個執行緒才能獲得物件鎖得到執行。如果不是同一個物件,則不會產生互斥
當synchronized作用在類時,對於同一個jvm中不同物件的多個執行緒呼叫同一個synchronized修飾的方法都是互斥的,因為一個jvm只會產生一個class檔案。
拓展:
1、如果想讓執行緒互斥,synchronized方法是否存在效率問題?
理論應該是存在效率問題的,因為每個物件都有一個物件鎖,當一個執行緒拿到鎖後,其它執行緒必須阻塞。(未寫程式碼測試)
2、如果是分散式的系統,使用synchronized無效了,因為synchronized最多隻能鎖住當前JVM的執行緒,對於其它server的執行緒無能為力。那麼怎麼處理呢?
查了些資料,網上說可以用zookeeper+其它元件完成分散式鎖或者樂觀鎖。由於沒有具體實踐過,只是看了幾篇文章,沒有發言權,所以感興趣的朋友可以自行搜尋 java+分散式鎖