歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java多線程之Synchronized關鍵字--對象鎖的特點

Java多線程之Synchronized關鍵字--對象鎖的特點

日期:2017/3/1 9:16:12   编辑:Linux編程

一,介紹

本文介紹Java多線程中的synchronized關鍵字作為對象鎖的一些知識點。

所謂對象鎖,就是就是synchronized 給某個對象加鎖。關於 對象鎖 可參考:這篇文章 http://www.linuxidc.com/Linux/2016-05/131108.htm

二,分析

synchronized可以修飾實例方法,如下形式:

1 public class MyObject {
2 
3     synchronized public void methodA() {
4         //do something....
5     }

這裡,synchronized 關鍵字鎖住的是當前對象。這也是稱為對象鎖的原因。

為啥鎖住當前對象?因為 methodA()是個實例方法,要想執行methodA(),需要以 對象.方法() 的形式進行調用(obj.methodA(),obj是MyObject類的一個對象,synchronized就是把obj這個對象加鎖了)。

上面代碼也可寫成這樣:

1 public class MyObject {
2 
3     public void methodA() {
4         synchronized(this){
5             //do something....
6         }
7     } 

三,特點

使用synchronized關鍵字同步一個明顯的特點是:MyObject類中定義有多個synchronized修飾的實例方法時,若多個線程擁有同一個MyObject類的對象則這些方法只能以同步的方式執行。即,執行完一個synchronized修飾的方法後,才能執行另一個synchronized修飾的方法。

如下:

 1 public class MyObject {
 2 
 3     synchronized public void methodA() {
 4         //do something....
 5     }
 6 
 7     synchronized public void methodB() {
 8         //do some other thing
 9     }
10 }

MyObject類中有兩個synchronized修飾的方法。

 1 public class ThreadA extends Thread {
 2 
 3     private MyObject object;
 4 //省略構造方法
 5     @Override
 6     public void run() {
 7         super.run();
 8         object.methodA();
 9     }
10 }

線程A執行methodA()

public class ThreadB extends Thread {

    private MyObject object;
//省略構造方法
    @Override
    public void run() {
        super.run();
        object.methodB();
    }
}

線程B執行methodB()

public class Run {
    public static void main(String[] args) {
        MyObject object = new MyObject();

        //線程A與線程B 持有的是同一個對象:object
        ThreadA a = new ThreadA(object);
        ThreadB b = new ThreadB(object);
        a.start();
        b.start();
    }
}

由於線程A和線程B持有同一個MyObject類的對象object,盡管這兩個線程需要調用不同的方法,但是必須是同步的,比如:線程B需要等待線程A執行完了methodA()方法之後,它才能執行methodB()方法。

四,結論

從上可以看出,本文中講述的 synchronized 鎖的范圍是整個對象。如果一個類中有多個synchronized修飾的同步方法,且多個線程持有該類的同一個對象(該類的相同的對象),盡管它們調用不同的方法,各個方法的執行也是同步的。

如果各個同步的方法之間沒有共享變量,或者說各個方法之間沒有聯系,但也只能同步執行,這會影響效率。

五,應用--使用synchronized避免 因數據不一致性而導致讀髒數據的情況

如下示例:

 1 public class MyObject {
 2 
 3     private String userName = "b";
 4     private String passWord = "bb";
 5     
 6     synchronized public void methodA(String userName, String passWord) {
 7         this.userName = userName;
 8         try{
 9             Thread.sleep(5000);
10         }catch(InterruptedException e){
11             
12         }
13         this.passWord = passWord;
14     }
15 
16     synchronized public void methodB() {
17         System.out.println("userName" + userName + ": " + "passWord" + passWord);
18     }
19 }

methodA()負責更改用戶名和密碼。在現實中,一個用戶名對應著一個密碼。。。

methodB()負責讀取用戶名和密碼。

如果methodB()沒有用synchronized 修飾,線程A在調用methodA()執行到第7行,更改了用戶名,因某種原因(比如在第9行睡眠了)放棄了CPU。

此時,如果線程B去執行methodB(),那麼讀取到的用戶名是線程A更改了的用戶名("a"),但是密碼卻是原來的密碼("bb")。因為,線程A睡眠了,還沒有來得及更改密碼。

但是,如果methodB()用synchronized修飾,那麼線程B只能等待線程A執行完畢之後(即改了用戶名,也改了密碼),才能執行methodB讀取用戶名和密碼。因此,就避免了數據的不一致性而導致的髒讀問題。

Copyright © Linux教程網 All Rights Reserved