靜態方法加鎖,和非靜態方法加鎖區別


原文地址:http://greemranqq.iteye.com/blog/1974143

今天看了到有意思的題:在靜態方法上加鎖 和 非靜態方法加鎖 有什么區別,從而再次引出鎖機制的一些理解。
先看方法:

// 這是一個很簡單的類,里面共享靜態變量 num,然后一個靜態 和 非靜態方法,都加上鎖
// 我們假設有兩個線程同時操作這兩個方法,那么數據能互斥嗎?

Java代碼

public class Walk {  
    public static int num = 100;  
    public static Walk walk = new Walk();  
    // 靜態 
    public synchronized static   int run(){  
            int i = 0;  
            while (i < 10) {  
                try {  
                    num --;  
                    i++;  
                    System.out.println(Thread.currentThread().getName()+":"+num);  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
            return num ;  
    }  
    // 非靜態 
    public  synchronized int  walk(){  
            int i = 0;  
            while (i < 10) {  
                try {  
                    num --;  
                    i++;  
                    System.out.println(Thread.currentThread().getName()+":"+num);  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
            return num ;  
    }  
}  
// 先建立兩個測試類,這里我們默認循環10次 
public class T3 implements Runnable {  
    @Override  
    public void run() {  
        Walk walk = new Walk();  
        //Walk walk = Walk.walk; 
        walk.walk();  
    }  
}  

public class T1 implements Runnable{  
    @Override  
    public void run() {  
        Walk walk = new Walk();  
        //Walk walk = Walk.walk; 
        // 這里我依然用的new 
        walk.run();  
    }  
}  

Java代碼

// 測試方法 
public class Test {  
    public static void main(String[] args) {  
        Thread t1 = new  Thread(new T1());  
        Thread t3 = new  Thread(new T3());  
        ExecutorService es = Executors.newCachedThreadPool();  
        es.execute(t1);  
        es.execute(t3);  
        es.shutdown();  
    }  
}  

// 測試數據 我就不完全列出了

pool-1-thread-1:98
pool-1-thread-2:98
pool-1-thread-2:97
pool-1-thread-1:96
…..
可以看出兩個線程沒有互斥,這是為什么呢?
OK,我們將static 關鍵字去掉,代碼我就不貼了,直接看結果。。
pool-1-thread-1:98
pool-1-thread-2:98
pool-1-thread-2:96

結果還是沒有出現互斥現象,因此我們默認要先讓一個線程執行10次的,假設我們這個是買票系統這是不允許的。為什么會出現這狀況呢,方法都加上的鎖的。

這里先引一下鎖的理解,然后從后向前解釋。
JAVA 的鎖機制說明:每個對象都有一個鎖,並且是唯一的。假設分配的一個對象空間,里面有多個方法,相當於空間里面有多個小房間,如果我們把所有的小房間都加鎖,因為這個對象只有一把鑰匙,因此同一時間只能有一個人打開一個小房間,然后用完了還回去,再由JVM 去分配下一個獲得鑰匙的人。

第二次實驗,我們是對方法進行加鎖了,但是沒得到想要的結果,原因在於房間與鑰匙。因為我們每個線程在調用方法的時候都是new 一個對象,那么就會出現兩個空間,兩把鑰匙,而靜態變量只有一個,相當於我們有兩把鑰匙,從不同的房間開門取共享的值,因此出錯。

如果我們使用靜態變量walk 呢?這代碼放開,也就是我們統一使用一個對象去操作變量,那么結果..

使用 Walk.walk.walk(); 和 Walk.run();

結果:還是沒有互斥
pool-1-thread-1:99
pool-1-thread-2:98
pool-1-thread-1:97

如果我們把靜態方法關鍵字 去掉: 就可以看見互斥現象了

pool-1-thread-1:99
pool-1-thread-1:98
pool-1-thread-1:96

結果發現還是會重復,因此我們可以得出,在靜態方法上加鎖,和普通方法上加鎖,他們用的不是同一把所,不是同一把鑰匙。從而得出 他們的對象鎖是不同的,對象也是不同的。

這里再次引出一個概念:對象鎖 和 類鎖

對象鎖:JVM 在創建對象的時候,默認會給每個對象一把唯一的對象鎖,一把鑰匙
類鎖:每一個類都是一個對象,每個對象都擁有一個對象鎖。

呵呵,概念感覺混淆了,其實都是鎖,取兩個名詞,下面區分方便,效果是一樣的,如果我們這樣實現。

Java代碼

// 靜態,這里僅僅將方法所 變成了 類鎖。 
    public  static int run(){  
        synchronized(Walk.class) {  
            int i = 0;  
            while (i < 10) {  
                try {  
                    num --;  
                    i++;  
                    System.out.println(Thread.currentThread().getName()+":"+num);  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
            return num ;  
        }  
    }  

結果:
pool-1-thread-1:98
pool-1-thread-2:98
pool-1-thread-2:97
pool-1-thread-1:97

發現結果還是不是互斥的,說明在靜態方法上加鎖,和 實例方法加鎖,對象鎖 其實不一樣的。如果我們改成:
synchronized(walk) {
//….略
}

結果:
pool-1-thread-2:99
pool-1-thread-2:98
pool-1-thread-2:97
這樣就互斥了,因為T1 是通過靜態變量walk 調用的,默認就是用的walk 對象這把鎖,而靜態方法 強制讓他也使用 walk這把鎖,就出現了互斥現象,因為鑰匙只有一把。

如果我們兩個方法都是靜態方法呢?
..
小結:
1.對象鎖鑰匙只能有一把才能互斥,才能保證共享變量的唯一性
2.在靜態方法上的鎖,和 實例方法上的鎖,默認不是同樣的,如果同步需要制定兩把鎖一樣。
3.關於同一個類的方法上的鎖,來自於調用該方法的對象,如果調用該方法的對象是相同的,那么鎖必然相同,否則就不相同。比如 new A().x() 和 new A().x(),對象不同,鎖不同,如果A的單利的,就能互斥。
4.靜態方法加鎖,能和所有其他靜態方法加鎖的 進行互斥
5.靜態方法加鎖,和xx.class 鎖效果一樣,直接屬於類的


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com