同步監視器之同步代碼塊、同步方法,同步監視器

來源:互聯網
上載者:User

同步監視器之同步代碼塊、同步方法,同步監視器

如果有多個線程訪問共用資源,可能會出現當一個線程沒有處理完業務,然後另一個線程進入,從而導致共用資源出現不安全的情況。

 

日常例子:銀行取錢,A和B有擁有同一個銀行賬戶,A用存摺在櫃檯取錢,B在取款機取錢。取錢有兩個關鍵步驟:

    (1)判斷賬戶裡的錢的餘額是否大於所取錢數

    (2)如果大於所取錢數,則賬戶最終所剩餘額 = 餘額 - 所取錢數。

如果沒有線程同步的情況下,我們假設這一種情況,這個共同的賬戶裡共1000元。

    (1)A  B同時去取600元,A所線上程執行到上面的第一個步驟,判斷所取錢數小於現有餘額,CPU時間片用完。

    (2)這時B進來到第一個步驟,同樣是執行判斷,因為A只執行完第一步驟,沒有執行減法,這時現有餘額還是1000元。

    (3)由於在CPU分配的時間裡他接著完成了減法操作。這時賬戶餘額為1000 - 600 = 400。成功取出600元。

    (4)最後A接著之前執行的步驟,去做減法操作, 賬戶餘額為 -200 = 400 - 600。

到這裡,我只想說為什麼,是什麼銀行可以允許你這麼做, 當然,除非銀行是你家開的。

總之銀行不可能讓這種情況發生,所以我們的偉大先賢們就想到線程同步,其實很簡單,你也能想到。如果讓這兩個步驟同時完成,不可分開,問題也就迎刃而解。

 

下面就說到在JAVA中同步代碼的實現:

  涉及概念:同步監視器,是一個普通的java對象,同一個同步監視器如果一個線程拿到,則其他線程就沒有辦法拿到。好像是一個房門裡只有唯一的一把鑰匙, 不能複製。如果一個人拿著它進入房門,其他人只能在外面等候。等他出來你獲得了它,你才能進入房間。

下面的代碼如果沒有做線程同步操作(同步代碼塊、同步方法、同步鎖)結果是如下:

  Thread-1------判斷所取錢數是否大於餘額------
  Thread-0------判斷所取錢數是否大於餘額------
  Thread-0======做減法操作,取出現金======
  Thread-1======做減法操作,取出現金======

很顯然線程1的那兩步沒有同時完成。

 

下面的幾種方法可以實現兩步同時完成。

1、同步代碼塊:

public class ThreadTest {

  public static void main(String[] args){
    Thread t1 = new Thread1(); //線程1
    Thread t2 = new Thread1();//線程2
    t1.start();
    t2.start();
  }
}

class Thread1 extends Thread{

  @Override
  public void run() {
    super.run();
    try {

      BeTested b = new BeTested(); // 這地方,因為這個例子中同步監視器 obj 是線程共用的,兩個線程用兩個不同的對象,也沒有關係,不影響結果。
      b.beTested(this);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class BeTested {
  static Object obj = new Object();;
  public void beTested(Thread t) throws InterruptedException{
    synchronized (obj) { //  obj 為同步監視器
      System.out.println(t.getName() + "------判斷所取錢數是否大於餘額------");
      t.sleep(1000); //  如果沒有同步這樣能理明顯地看到這兩步驟不能在一個線程,同一個時間片裡執行完成。
      System.out.println(t.getName() + "======做減法操作,取出現金======");
    }
  }
}

執行結果如下:

Thread-0------判斷所取錢數是否大於餘額------
Thread-0======做減法操作,取出現金======
Thread-1------判斷所取錢數是否大於餘額------
Thread-1======做減法操作,取出現金======

注意:同步監視器對象的選用很關鍵。要選擇線程共用的對象,比如上面例子的 obj, 它是static修飾的才行,如果沒有static修飾,則是使用不同的同步監視器(不是同一個對象),相當於是兩把鑰匙。

  (如果obj = "aaaa" 沒有static修飾也可以實現同步,那是因為這個obj引用的常量池裡的同一個string對象,強烈不推薦使用)

 

2、同步方法(非靜態方法)

把上面的那兩類改成如下,main方法所在類不變。

class Thread1 extends Thread{

  static BeTested b = new BeTested();  // 在這種方法中,這裡必須是同個對象(static修飾),下文會詳細說明
  @Override
  public void run() {
    super.run();
    try {
      b.beTested(this);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class BeTested {
  static Object obj = new Object();;
  public synchronized void beTested(Thread t) throws InterruptedException{
      System.out.println(t.getName() + "------判斷所取錢數是否大於餘額------");
      t.sleep(1000); 
      System.out.println(t.getName() + "======做減法操作,取出現金======");
  }
}

執行結果如下:

Thread-0------判斷所取錢數是否大於餘額------
Thread-0======做減法操作,取出現金======
Thread-1------判斷所取錢數是否大於餘額------
Thread-1======做減法操作,取出現金======

注意:因為同步方法中,所用的同步監視器不能指定,預設使用的調用該方法的對象,也就是this。所以 Thread1 類中相對於樣本1中同步代碼塊中修改的部分, 也是要static修飾。也就是說要使用同一個對象。

 

3、同步方法(靜態方法)

把上面的那兩類改成如下,main方法所在類不變。

class Thread1 extends Thread{

  @Override
  public void run() {
    super.run();
    try {
      BeTested b = new BeTested(); // 這裡每個線程使用不同的對象。
      b.beTested(this);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class BeTested {
  static Object obj = new Object();;
  public static synchronized void beTested(Thread t) throws InterruptedException{
      System.out.println(t.getName() + "------判斷所取錢數是否大於餘額------");
      t.sleep(1000); 
      System.out.println(t.getName() + "======做減法操作,取出現金======");
  }
}

執行結果如下:

Thread-0------判斷所取錢數是否大於餘額------
Thread-0======做減法操作,取出現金======
Thread-1------判斷所取錢數是否大於餘額------
Thread-1======做減法操作,取出現金======

注意:因為同步靜態方法中,同步監視器是這個類而不是這個類的對象。所以Thread1 類中相對於樣本2中同步代碼塊中修改的部分,不須要用static修飾,不是同一個對象也沒關係。因為這個類他本身就是共用的。

 

總結:如上幾種方式進行線程同步處理時,要注意你所使用的同步監視器對象,它必須是共用的。

 

註:還有使用同步鎖的方式實現線程同步,本篇文章不做討論。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.