激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 解析Java線程編程中的線程安全與synchronized的使用

解析Java線程編程中的線程安全與synchronized的使用

2020-03-06 20:05海子 JAVA教程

這篇文章主要介紹了Java線程編程中的線程安全與synchronized的使用,synchronized多線程使用時一定要注意線程之間的沖突問題,需要的朋友可以參考下

一.什么時候會出現線程安全問題?

  在單線程中不會出現線程安全問題,而在多線程編程中,有可能會出現同時訪問同一個資源的情況,這種資源可以是各種類型的的資源:一個變量、一個對象、一個文件、一個數據庫表等,而當多個線程同時訪問同一個資源的時候,就會存在一個問題:

  由于每個線程執行的過程是不可控的,所以很可能導致最終的結果與實際上的愿望相違背或者直接導致程序出錯。

  舉個簡單的例子:

  現在有兩個線程分別從網絡上讀取數據,然后插入一張數據庫表中,要求不能插入重復的數據。

  那么必然在插入數據的過程中存在兩個操作:

  1)檢查數據庫中是否存在該條數據;

  2)如果存在,則不插入;如果不存在,則插入到數據庫中。

  假如兩個線程分別用thread-1和thread-2表示,某一時刻,thread-1和thread-2都讀取到了數據X,那么可能會發生這種情況:

  thread-1去檢查數據庫中是否存在數據X,然后thread-2也接著去檢查數據庫中是否存在數據X。

  結果兩個線程檢查的結果都是數據庫中不存在數據X,那么兩個線程都分別將數據X插入數據庫表當中。

  這個就是線程安全問題,即多個線程同時訪問一個資源時,會導致程序運行結果并不是想看到的結果。

  這里面,這個資源被稱為:臨界資源(也有稱為共享資源)。

  也就是說,當多個線程同時訪問臨界資源(一個對象,對象中的屬性,一個文件,一個數據庫等)時,就可能會產生線程安全問題。

  不過,當多個線程執行一個方法,方法內部的局部變量并不是臨界資源,因為方法是在棧上執行的,而Java棧是線程私有的,因此不會產生線程安全問題。

二.如何解決線程安全問題?

  那么一般來說,是如何解決線程安全問題的呢?

  基本上所有的并發模式在解決線程安全問題時,都采用“序列化訪問臨界資源”的方案,即在同一時刻,只能有一個線程訪問臨界資源,也稱作同步互斥訪問。

  通常來說,是在訪問臨界資源的代碼前面加上一個鎖,當訪問完臨界資源后釋放鎖,讓其他線程繼續訪問。

  在Java中,提供了兩種方式來實現同步互斥訪問:synchronized和Lock。

  本文主要講述synchronized的使用方法,Lock的使用方法在下一篇博文中講述。

三.synchronized同步方法或者同步塊

  在了解synchronized關鍵字的使用方法之前,我們先來看一個概念:互斥鎖,顧名思義:能到達到互斥訪問目的的鎖。

  舉個簡單的例子:如果對臨界資源加上互斥鎖,當一個線程在訪問該臨界資源時,其他線程便只能等待。

  在Java中,每一個對象都擁有一個鎖標記(monitor),也稱為監視器,多線程同時訪問某個對象時,線程只有獲取了該對象的鎖才能訪問。

  在Java中,可以使用synchronized關鍵字來標記一個方法或者代碼塊,當某個線程調用該對象的synchronized方法或者訪問synchronized代碼塊時,這個線程便獲得了該對象的鎖,其他線程暫時無法訪問這個方法,只有等待這個方法執行完畢或者代碼塊執行完畢,這個線程才會釋放該對象的鎖,其他線程才能執行這個方法或者代碼塊。

  下面通過幾個簡單的例子來說明synchronized關鍵字的使用:

  1.synchronized方法

  下面這段代碼中兩個線程分別調用insertData對象插入數據:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Test {
 
  public static void main(String[] args) {
    final InsertData insertData = new InsertData();
     
    new Thread() {
      public void run() {
        insertData.insert(Thread.currentThread());
      };
    }.start();
     
     
    new Thread() {
      public void run() {
        insertData.insert(Thread.currentThread());
      };
    }.start();
  }
}
 
class InsertData {
  private ArrayList<Integer> arrayList = new ArrayList<Integer>();
   
  public void insert(Thread thread){
    for(int i=0;i<5;i++){
      System.out.println(thread.getName()+"在插入數據"+i);
      arrayList.add(i);
    }
  }
}

  此時程序的輸出結果為:

解析Java線程編程中的線程安全與synchronized的使用

說明兩個線程在同時執行insert方法。

  而如果在insert方法前面加上關鍵字synchronized的話,運行結果為:

?
1
2
3
4
5
6
7
8
9
10
class InsertData {
  private ArrayList<Integer> arrayList = new ArrayList<Integer>();
   
  public synchronized void insert(Thread thread){
    for(int i=0;i<5;i++){
      System.out.println(thread.getName()+"在插入數據"+i);
      arrayList.add(i);
    }
  }
}

解析Java線程編程中的線程安全與synchronized的使用

從上輸出結果說明,Thread-1插入數據是等Thread-0插入完數據之后才進行的。說明Thread-0和Thread-1是順序執行insert方法的。

  這就是synchronized方法。

  不過有幾點需要注意:

  1)當一個線程正在訪問一個對象的synchronized方法,那么其他線程不能訪問該對象的其他synchronized方法。這個原因很簡單,因為一個對象只有一把鎖,當一個線程獲取了該對象的鎖之后,其他線程無法獲取該對象的鎖,所以無法訪問該對象的其他synchronized方法。

  2)當一個線程正在訪問一個對象的synchronized方法,那么其他線程能訪問該對象的非synchronized方法。這個原因很簡單,訪問非synchronized方法不需要獲得該對象的鎖,假如一個方法沒用synchronized關鍵字修飾,說明它不會使用到臨界資源,那么其他線程是可以訪問這個方法的,

  3)如果一個線程A需要訪問對象object1的synchronized方法fun1,另外一個線程B需要訪問對象object2的synchronized方法fun1,即使object1和object2是同一類型),也不會產生線程安全問題,因為他們訪問的是不同的對象,所以不存在互斥問題。

  2.synchronized代碼塊

  synchronized代碼塊類似于以下這種形式:

synchronized(synObject) {
        
    }
  當在某個線程中執行這段代碼塊,該線程會獲取對象synObject的鎖,從而使得其他線程無法同時訪問該代碼塊。

  synObject可以是this,代表獲取當前對象的鎖,也可以是類中的一個屬性,代表獲取該屬性的鎖。

  比如上面的insert方法可以改成以下兩種形式:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class InsertData {
  private ArrayList<Integer> arrayList = new ArrayList<Integer>();
   
  public void insert(Thread thread){
    synchronized (this) {
      for(int i=0;i<100;i++){
        System.out.println(thread.getName()+"在插入數據"+i);
        arrayList.add(i);
      }
    }
  }
}
 
class InsertData {
  private ArrayList<Integer> arrayList = new ArrayList<Integer>();
  private Object object = new Object();
   
  public void insert(Thread thread){
    synchronized (object) {
      for(int i=0;i<100;i++){
        System.out.println(thread.getName()+"在插入數據"+i);
        arrayList.add(i);
      }
    }
  }
}

  從上面可以看出,synchronized代碼塊使用起來比synchronized方法要靈活得多。因為也許一個方法中只有一部分代碼只需要同步,如果此時對整個方法用synchronized進行同步,會影響程序執行效率。而使用synchronized代碼塊就可以避免這個問題,synchronized代碼塊可以實現只對需要同步的地方進行同步。

  另外,每個類也會有一個鎖,它可以用來控制對static數據成員的并發訪問。

  并且如果一個線程執行一個對象的非static synchronized方法,另外一個線程需要執行這個對象所屬類的static synchronized方法,此時不會發生互斥現象,因為訪問static synchronized方法占用的是類鎖,而訪問非static synchronized方法占用的是對象鎖,所以不存在互斥現象。

看下面這段代碼就明白了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Test {
 
  public static void main(String[] args) {
    final InsertData insertData = new InsertData();
    new Thread(){
      @Override
      public void run() {
        insertData.insert();
      }
    }.start();
    new Thread(){
      @Override
      public void run() {
        insertData.insert1();
      }
    }.start();
  }
}
 
class InsertData {
  public synchronized void insert(){
    System.out.println("執行insert");
    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("執行insert完畢");
  }
   
  public synchronized static void insert1() {
    System.out.println("執行insert1");
    System.out.println("執行insert1完畢");
  }
}

  執行結果;

解析Java線程編程中的線程安全與synchronized的使用

第一個線程里面執行的是insert方法,不會導致第二個線程執行insert1方法發生阻塞現象。

  下面我們看一下synchronized關鍵字到底做了什么事情,我們來反編譯它的字節碼看一下,下面這段代碼反編譯后的字節碼為:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class InsertData {
  private Object object = new Object();
   
  public void insert(Thread thread){
    synchronized (object) {
     
    }
  }
   
  public synchronized void insert1(Thread thread){
     
  }
   
  public void insert2(Thread thread){
     
  }
}

解析Java線程編程中的線程安全與synchronized的使用

 從反編譯獲得的字節碼可以看出,synchronized代碼塊實際上多了monitorenter和monitorexit兩條指令。monitorenter指令執行時會讓對象的鎖計數加1,而monitorexit指令執行時會讓對象的鎖計數減1,其實這個與操作系統里面的PV操作很像,操作系統里面的PV操作就是用來控制多個線程對臨界資源的訪問。對于synchronized方法,執行中的線程識別該方法的 method_info 結構是否有 ACC_SYNCHRONIZED 標記設置,然后它自動獲取對象的鎖,調用方法,最后釋放鎖。如果有異常發生,線程自動釋放鎖。

有一點要注意:對于synchronized方法或者synchronized代碼塊,當出現異常時,JVM會自動釋放當前線程占用的鎖,因此不會由于異常導致出現死鎖現象。

三.關于synchronized的其他一些值得注意的地方

1.synchronized與static synchronized 的區別
  synchronized是對類的當前實例進行加鎖,防止其他線程同時訪問該類的該實例的所有synchronized塊,注意這里是“類的當前實例”,類的兩個不同實例就沒有這種約束了。那么static synchronized恰好就是要控制類的所有實例的訪問了,static synchronized是限制線程同時訪問jvm中該類的所有實例同時訪問對應的代碼快。實際上,在類中某方法或某代碼塊中有synchronized,那么在生成一個該類實例后,該類也就有一個監視快,放置線程并發訪問改實例synchronized保護快,而static synchronized則是所有該類的實例公用一個監視快了,也就是兩個的區別了,也就是synchronized相當于this.synchronized,而
  static synchronized相當于Something.synchronized.
  一個日本作者-結成浩的《java多線程設計模式》有這樣的一個列子:
  

?
1
2
3
4
5
6
pulbic class Something(){
  public synchronized void isSyncA(){}
  public synchronized void isSyncB(){}
  public static synchronized void cSyncA(){}
  public static synchronized void cSyncB(){}
  }

  那么,加入有Something類的兩個實例a與b,那么下列組方法何以被1個以上線程同時訪問呢
  a.   x.isSyncA()與x.isSyncB()
  b.   x.isSyncA()與y.isSyncA()
  c.   x.cSyncA()與y.cSyncB()
  d.   x.isSyncA()與Something.cSyncA()
  這里,很清楚的可以判斷:
  a,都是對同一個實例的synchronized域訪問,因此不能被同時訪問
  b,是針對不同實例的,因此可以同時被訪問
  c,因為是static synchronized,所以不同實例之間仍然會被限制,相當于Something.isSyncA()與   Something.isSyncB()了,因此不能被同時訪問。
  那么,第d呢?,書上的 答案是可以被同時訪問的,答案理由是synchronzied的是實例方法與synchronzied的類方法由于鎖定(lock)不同的原因。
  個人分析也就是synchronized 與static synchronized 相當于兩幫派,各自管各自,相互之間就無約束了,可以被同時訪問。目前還不是分清楚java內部設計synchronzied是怎么樣實現的。
  結論:A: synchronized static是某個類的范圍,synchronized static cSync{}防止多個線程同時訪問這個    類中的synchronized static 方法。它可以對類的所有對象實例起作用。
  B: synchronized 是某實例的范圍,synchronized isSync(){}防止多個線程同時訪問這個實例中的synchronized 方法。
  2.synchronized方法與synchronized代碼快的區別
  synchronized methods(){} 與synchronized(this){}之間沒有什么區別,只是 synchronized methods(){} 便于閱讀理解,而synchronized(this){}可以更精確的控制沖突限制訪問區域,有時候表現更高效率。
  3.synchronized關鍵字是不能繼承的

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产美女视频一区二区三区 | 午夜看毛片 | 一级视频在线播放 | 色3344| 国内精品伊人久久 | 亚洲一级片在线观看 | 中文字幕极速在线观看 | 午夜视| 久久最新网址 | 欧美精品免费一区二区三区 | 色女生影院| 国产精品夜色视频一级区 | 精彩视频一区 | 国产成人在线观看免费网站 | 视频二区国产 | 韩国精品视频在线观看 | 久久久久久久亚洲精品 | 亚洲成人国产综合 | 国产精品久久久久久久久久尿 | 91成人在线网站 | 国产亚洲精久久久久久蜜臀 | 国产成人在线视频 | 国产一区二区精品在线观看 | av在线网站观看 | 永久免费在线观看av | 黄色片视频在线观看 | 亚洲一区播放 | 日韩视频在线观看免费 | 4p嗯啊巨肉寝室调教男男视频 | 黄色片免费在线 | 最新亚洲视频 | 亚洲视频精品在线 | 欧美日韩免费在线观看视频 | 羞羞视频免费网站含羞草 | 亚洲欧美日韩精品久久 | 最新在线中文字幕 | 欧美不卡三区 | 欧美在线成人影院 | av电影网站在线 | 久久人人爽人人爽人人片av高清 | 视频精品久久 |