Java中的原子性操做與同步問題java
1.在Java中,原子操做是指不能被線程調度機制中斷的操做,一旦操做開始,那麼它必定能夠在可能發生的"上下文切換"以前(切換到其它線程執行)執行完畢。ide
2.依賴原子性來處理同步問題時很棘手而且很危險的事情。spa
以下面的程序,儘管return i;確實是原子性操做,可是缺乏同步使得其數值能夠在處於不穩定的中間狀態時被讀取,儘管變量i添加上了volatile關鍵字。線程
正確的方法時同時將getValue()和evenIncrement()都是synchronized的。3d
1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 4 5 public class AtomicityTest implements Runnable { 6 7 private int i = 0; 8 9 public int getValue() { 10 return i; 11 } 12 13 private synchronized void evenIncrement() { 14 i++; 15 i++; 16 } 17 18 @Override 19 public void run() { 20 while (true) { 21 evenIncrement(); 22 } 23 } 24 25 public static void main(String[] args) { 26 ExecutorService exec = Executors.newCachedThreadPool(); 27 AtomicityTest at = new AtomicityTest(); 28 exec.execute(at); 29 while (true) { 30 int val = at.getValue(); 31 if (val % 2 != 0) { 32 System.out.println("error..." + val + " is odd."); 33 System.exit(0); 34 } 35 } 36 37 } 38 39 } 40 /** 41 * 程序運行結果: 42 * error...53683 is odd. 43 */
再以下面的程序,一方面雖然用volatile關鍵字修飾了SerialNumberGenerator.serialNumber成員,可是nextSerialNumber()中的++運算並非原子操做,因此仍是會出現問題。code
解決問題得方法就是把nextSerialNumber()方法改成synchronized的。blog
1 public class SerialNumberGenerator { 2 3 private static volatile int serialNumber = 0; 4 5 public static int nextSerialNumber() { 6 return serialNumber++; 7 } 8 }
1 public class CircularSet { 2 3 private int[] array; 4 private int len; 5 private int index = 0; 6 7 public CircularSet(int size) { 8 array = new int[size]; 9 len = size; 10 for (int i = 0; i < len; i++) array[i] = -1; 11 } 12 13 public synchronized void add(int i) { 14 array[index] = i; 15 index = (index + 1) % len; 16 } 17 18 public synchronized boolean contains(int val) { 19 for (int i = 0; i < len; i++) { 20 if (array[i] == val) return true; 21 } 22 return false; 23 } 24 25 }
1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 4 5 public class SerialNumberChecker { 6 7 private static final int SIZE = 10; 8 private static CircularSet serials = new CircularSet(1000); 9 private static ExecutorService exec = Executors.newCachedThreadPool(); 10 11 public static void main(String[] args) { 12 13 for (int i = 0; i < SIZE; i++) { 14 exec.execute(new Runnable() { 15 16 @Override 17 public void run() { 18 while (true) { 19 int serial = SerialNumberGenerator.nextSerialNumber(); 20 if (serials.contains(serial)) { 21 System.out.println("error... " + serial + " duplicate."); 22 System.exit(0); 23 } 24 serials.add(serial); 25 } 26 } 27 28 }); 29 } 30 } 31 32 } 33 34 /** 35 * 程序執行結果: 36 * error... 8298 duplicate. 37 * error... 9049 duplicate. 38 * error... 9051 duplicate. 39 * error... 9045 duplicate. 40 * error... 9050 duplicate. 41 * error... 9048 duplicate. 42 * error... 9047 duplicate. 43 * error... 9046 duplicate. 44 */