程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> java synchronized症結字的用法

java synchronized症結字的用法

編輯:關於JAVA

java synchronized症結字的用法。本站提示廣大學習愛好者:(java synchronized症結字的用法)文章只能為提供參考,不一定能成為您想要的結果。以下是java synchronized症結字的用法正文


0.先導的成績代碼

    上面的代碼演示了一個計數器,兩個線程同時對i停止累加的操作,各履行1000000次.我們希冀的成果確定是i=2000000.然則我們屢次履行今後,會發明i的值永久小於2000000.這是由於,兩個線程同時對i停止寫入的時刻,個中一個線程的成果會籠罩別的一個.

public class AccountingSync implements Runnable {
  static int i = 0;
  public void increase() {
    i++;
  }
 
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      increase();
    }
  }
 
  public static void main(String[] args) throws InterruptedException {
    AccountingSync accountingSync = new AccountingSync();
 
    Thread t1 = new Thread(accountingSync);
    Thread t2 = new Thread(accountingSync);
 
    t1.start();
    t2.start();
 
    t1.join();
    t2.join();
 
    System.out.println(i);
  }
}

    要從基本上處理這個成績,我們必需包管多個線程在對i停止操作的時刻,要完整的同步.也就是說到A線程對i停止寫入的時刻,B線程不只弗成以寫入,連讀取都弗成以.

1.synchronized症結字的感化

    症結字synchronized的感化其實就是完成線程間的同步.它的任務就是對同步的代碼停止加鎖,使得每次,只能有一個線程進入同步塊,從而包管線程間的平安性.就像下面的代碼中,i++的操作只能同時又一個線程在履行.

2.synchronized症結字的用法

指定對象加鎖:對給定的對象停止加鎖,進入同步代碼塊要取得給定對象的鎖

直接感化於實例辦法:相當於對以後實例加鎖,進入同步代碼塊要取得以後實例的鎖(這請求創立Thread的時刻,要用統一個Runnable的實例才可以)

直接感化於靜態辦法:相當於給以後類加鎖,進入同步代碼塊前要取得以後類的鎖

2.1指定對象加鎖

    上面的代碼,將synchronized感化於一個給定的對象.這裡有一個留意的,給定對象必定如果static的,不然我們每次new一個線程出來,彼此其實不同享該對象,加鎖的意義也就不存在了.

public class AccountingSync implements Runnable {
  final static Object OBJECT = new Object();
 
  static int i = 0;
  public void increase() {
    i++;
  }
 
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      synchronized (OBJECT) {
        increase();
      }
    }
  }
 
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(new AccountingSync());
    Thread t2 = new Thread(new AccountingSync());
 
    t1.start();
    t2.start();
 
    t1.join();
    t2.join();
 
    System.out.println(i);
  }
}
2.2直接感化於實例辦法

    synchronized症結字感化於實例辦法,就是說在進入increase()辦法之前,線程必需取得以後實例的鎖.這就請求我們,在創立Thread實例的時刻,要應用統一個Runnable的對象實例.不然,線程的鎖都不在統一個實例下面,無從去談加鎖/同步的成績了.

public class AccountingSync implements Runnable {
  static int i = 0;
  public synchronized void increase() {
    i++;
  }
 
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      increase();
    }
  }
 
  public static void main(String[] args) throws InterruptedException {
    AccountingSync accountingSync = new AccountingSync();
 
    Thread t1 = new Thread(accountingSync);
    Thread t2 = new Thread(accountingSync);
 
    t1.start();
    t2.start();
 
    t1.join();
    t2.join();
 
    System.out.println(i);
  }
}

    請留意main辦法的前三行,解釋症結字感化於實例辦法上的准確用法.

2.3直接感化於靜態辦法

    將synchronized症結字感化在static辦法上,就不消像下面的例子中,兩個線程要指向統一個Runnable辦法.由於辦法塊須要要求的是以後類的鎖,而不是以後實例,線程間照樣可以准確同步的.

public class AccountingSync implements Runnable {
  static int i = 0;
  public static synchronized void increase() {
    i++;
  }
 
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      increase();
    }
  }
 
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(new AccountingSync());
    Thread t2 = new Thread(new AccountingSync());
 
    t1.start();
    t2.start();
 
    t1.join();
    t2.join();
 
    System.out.println(i);
  }
}

3.毛病的加鎖

    從下面的例子裡,我們曉得,假如我們須要一個計數器運用,為了包管數據的准確性,我們天然會須要對計數器加鎖,是以,我們能夠會寫出上面的代碼:

public class BadLockOnInteger implements Runnable {
  static Integer i = 0;
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      synchronized (i) {
        i++;
      }
    }
  }
 
  public static void main(String[] args) throws InterruptedException {
    BadLockOnInteger badLockOnInteger = new BadLockOnInteger();
 
    Thread t1 = new Thread(badLockOnInteger);
    Thread t2 = new Thread(badLockOnInteger);
 
    t1.start();
    t2.start();
 
    t1.join();
    t2.join();
 
    System.out.println(i);
  }
}

    當我們運轉下面代碼的時刻,會發明輸入的i很小.這解釋線程並沒有平安.

    要說明這個成績,要從Integer說起:在Java中,Integer屬於不變對象,和String一樣,對象一旦被創立,就不克不及被修正了.假如你有一個Integer=1,那末它就永久都是1.假如你想讓這個對象=2呢?只能從新創立一個Integer.每次i++以後,相當於挪用了Integer的valueOf辦法,我們看一下Integer的valueOf辦法的源碼:

public static Integer valueOf(int i) {
  if (i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[i + (-IntegerCache.low)];
  return new Integer(i);
}

    Integer.valueOf()現實上是一個工場辦法,他會偏向於前往一個新的Integer對象,並把值從新復制給i;

    所以,我們就曉得成績地點的緣由,因為在多個線程之間,因為i++以後,i都指向了一個新的對象,所以線程每次加鎖能夠都加載了分歧的對象實例下面.處理辦法很簡略,應用下面的3種synchronize的辦法之一便可以處理了.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved