1.什麼是線程安全性?html
《Java Concurrency in Practice》中有提到:當多個線程訪問某個類時,這個類始終都能表現出正確的行爲,那麼就稱這個類是線程安全的。java
2.Java中的「同步」安全
Java中的主要同步機制是關鍵字「synchronized」,它提供了一種獨佔的加鎖方式,但「同步」這個術語還包括volatile類型的變量,顯式鎖(Explicit Lock)以及原子變量。多線程
2.原子性工具
原子是世界上的最小單位,具備不可分割性。好比 a=0;(a非long和double類型)這個操做是不可分割的,那麼咱們說這個操做時原子操做。再好比:a++;這個操做實際是a = a + 1;是可分割的,因此他不是一個原子操做。非原子操做都會存在線程安全問題,須要咱們使用同步技術(sychronized)來讓它變成一個原子操做。一個操做是原子操做,那麼咱們稱它具備原子性。java的concurrent包下提供了一些原子類,咱們能夠經過閱讀API來了解這些原子類的用法。好比:AtomicInteger、AtomicLong、AtomicReference等。atom
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 }
輸出結果:spa
static count: 9952 Counter: 10000 AtomicInteger: 10000 countVolatile: 9979
第一行與最後一行,每次運行將獲得不一樣的結果,可是中間兩行的結果相同。線程
經過上面的例子說明,要解決自增操做在多線程環境下線程不安全的問題,能夠選擇使用Java提供的原子類,或者使用synchronized同步方法。code
而經過Volatile關鍵字,並不能解決非原子操做的線程安全性。Volatile詳解htm
雖然遞增操做++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),並非咱們認爲的一條機器指令搞定的。