Java多線程之原子性 volatile、atomicInteger測試


原文鏈接:http://www.cnblogs.com/zhengbin/p/5653051.html

一、補充概念


1.什么是線程安全性?

  《Java Concurrency in Practice》中有提到:當多個線程訪問某個類時,這個類始終都能表現出正確的行為,那么就稱這個類是線程安全的。

2.Java中的“同步”

  Java中的主要同步機制是關鍵字“synchronized”,它提供了一種獨占的加鎖方式,但“同步”這個術語還包括volatile類型的變量,顯式鎖(Explicit Lock)以及原子變量。

2.原子性

  原子是世界上的最小單位,具有不可分割性。比如 a=0;(a非long和double類型)這個操作是不可分割的,那么我們說這個操作時原子操作。再比如:a++;這個操作實際是a = a + 1;是可分割的,所以他不是一個原子操作。非原子操作都會存在線程安全問題,需要我們使用同步技術(sychronized)來讓它變成一個原子操作。一個操作是原子操作,那么我們稱它具有原子性。java的concurrent包下提供了一些原子類,我們可以通過閱讀API來了解這些原子類的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。

二、實例源碼


 1 public class IncrementTestDemo {
 2 
 3     public static int count = 0;
 4     public static Counter counter = new Counter();
 5     public static AtomicInteger atomicInteger = new AtomicInteger(0);
 6     volatile public static int countVolatile = 0;
 7     
 8     public static void main(String[] args) {
 9         for (int i = 0; i < 10; i++) {
10             new Thread() {
11                 public void run() {
12                     for (int j = 0; j < 1000; j++) {
13                         count++;
14                         counter.increment();
15                         atomicInteger.getAndIncrement();
16                         countVolatile++;
17                     }
18                 }
19             }.start();
20         }
21         try {
22             Thread.sleep(3000);
23         } catch (InterruptedException e) {
24             e.printStackTrace();
25         }
26         
27         System.out.println("static count: " + count);
28         System.out.println("Counter: " + counter.getValue());
29         System.out.println("AtomicInteger: " + atomicInteger.intValue());
30         System.out.println("countVolatile: " + countVolatile);
31     }
32     
33 }
34 
35 class Counter {
36     private int value;
37 
38     public synchronized int getValue() {
39         return value;
40     }
41 
42     public synchronized int increment() {
43         return ++value;
44     }
45 
46     public synchronized int decrement() {
47         return --value;
48     }
49 }
復制代碼

  輸出結果:

+ View code
static count: 9952
Counter: 10000
AtomicInteger: 10000
countVolatile: 9979
 

  第一行與最后一行,每次運行將得到不同的結果,但是中間兩行的結果相同。

  通過上面的例子說明,要解決自增操作在多線程環境下線程不安全的問題,可以選擇使用Java提供的原子類,或者使用synchronized同步方法。

  而通過Volatile關鍵字,並不能解決非原子操作的線程安全性。Volatile詳解

三、Java中的自增原理


  雖然遞增操作++i是一種緊湊的語法,使其看上去只是一個操作,但這個操作並非原子的,因而它並不會作為一個不可分割的操作來執行。實際上,它包含了三個獨立的操作:讀取count的值,將值加1,然后將計算結果寫入count。這是一個“讀取 - 修改 - 寫入”的操作序列,並且其結果狀態依賴於之前的狀態。

  下面寫一個簡單的類,用jdk中的工具javap來反編譯Java字節碼文件。

/**
 * @author zhengbinMac
 */
public class TestDemo {
    public static int count;

    public void code() {
        count++;
    }
}
復制代碼
localhost:Increment zhengbinMac$ javap -c TestDemo
警告: 二進制文件TestDemo包含Increment.TestDemo
Compiled from "TestDemo.java"
public class Increment.TestDemo {
  public static int count;

  public Increment.TestDemo();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public void code();
    Code:
       0: getstatic     #2                  // Field count:I
       3: iconst_1      
       4: iadd          
       5: putstatic     #2                  // Field count:I
       8: return        
}
復制代碼

  如上字節碼,我們發現自增操作包括取數(getstatic  #2)、加一(iconst_1和iadd)、保存(putstatic  #2),並不是我們認為的一條機器指令搞定的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM