一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

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

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

服務器之家 - 編程語言 - JAVA教程 - java synchronized關鍵字的用法

java synchronized關鍵字的用法

2020-04-24 12:20wulei JAVA教程

synchronized關鍵字我們大家都知道是線程同步關鍵字.總結一下日常的使用方法,還有一個坑.

0.先導的問題代碼

    下面的代碼演示了一個計數器,兩個線程同時對i進行累加的操作,各執行1000000次.我們期望的結果肯定是i=2000000.但是我們多次執行以后,會發現i的值永遠小于2000000.這是因為,兩個線程同時對i進行寫入的時候,其中一個線程的結果會覆蓋另外一個.

?
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
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一個線程出來,彼此并不共享該對象,加鎖的意義也就不存在了.

 

?
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 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的對象實例.否則,線程的鎖都不在同一個實例上面,無從去談加鎖/同步的問題了.

?
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
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方法.因為方法塊需要請求的是當前類的鎖,而不是當前實例,線程間還是可以正確同步的.

?
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
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.錯誤的加鎖

    從上面的例子里,我們知道,如果我們需要一個計數器應用,為了保證數據的正確性,我們自然會需要對計數器加鎖,因此,我們可能會寫出下面的代碼:

?
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
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方法的源碼:

?
1
2
3
4
5
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的方法之一就可以解決了.

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲天天综合 | 大肚孕妇的高h辣文 | 国产精品吹潮香蕉在线观看 | 韩国最新理论三级在线观看 | 色婷婷久久综合中文久久一本 | 极品美女写真菠萝蜜视频 | 男人天堂亚洲 | 丁香网五月天 | 草莓视频旧版 | 91亚洲一区二区在线观看不卡 | 亚洲精品一二三四 | 久热人人综合人人九九精品视频 | 日韩高清在线观看 | 久久久黄色片 | 女人全身裸露无遮挡免费观看 | 第一次处破女18分钟 | jux539原千岁在线播放 | 亚洲伦理影院 | 把女的下面扒开添视频 | 精品区卡一卡2卡三免费 | 国内久久久 | 国模孕妇季玥337p人体 | 操娇妻| 女人又色又爽又黄 | 国产麻豆剧果冻传媒影视4934 | 91进入蜜桃臀在线播放 | 日韩欧美亚洲国产高清在线 | 大肥婆丰满大肥奶bbw肥 | 女子张腿让男人桶免费 | 四虎永久免费地址在线观看 | 欧美日韩视频在线成人 | 和老外3p爽粗大免费视频 | 亚洲高清影院 | 美女脱一光二净的视频 | 国产男女性特黄录像 | 国产午夜亚洲精品不卡 | 2018天天弄 | 国内自拍网红在综合图区 | 变态np虐高h| 美女班主任下面好爽好湿好紧 | 日韩欧美中文字幕出 |