現有業務場景需要做一個線程間的全局變量,並且實現自增效果。
初始使用了volatile 來保證count的安全性,如下:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class TestThredd { private volatile int count = 0; public void increment() { count++; } private int getCount() { return count; } /** * 這里模擬一個遞增的任務,遞增目標為50000 */ public static void main(String[] args) throws InterruptedException { final TestThredd counter = new TestThredd(); int workCount = 50000; ExecutorService executor = Executors.newFixedThreadPool(10); long start = System.currentTimeMillis(); for (int i = 0; i < workCount; i++) { Runnable runnable = new Runnable() { @Override public void run() { counter.increment(); } }; executor.execute(runnable); } // 關閉啟動線程,執行未完成的任務 executor.shutdown(); // 等待所有線程完成任務,完成后才繼續執行下一步 executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); System.out.println("耗時:" + (System.currentTimeMillis() - start) + "ms"); System.out.println("執行結果:count=" + counter.getCount()); } }
執行結果

它的結果不是我們預料的50000 .通常我們需要加上在count++時 加上synchronized關鍵字,保證他的正確性。
如下:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class TestThredd { private volatile int count = 0; /** * 為了保證數據的准確性,多線程的情況下需要加上synchronized關鍵字 * 否則會出現出乎預料的結果 這也是線程安全的重要體現 */ public synchronized void increment() { count++; } private int getCount() { return count; } /** * 這里模擬一個遞增的任務,遞增目標為50000 */ public static void main(String[] args) throws InterruptedException { final TestThredd counter = new TestThredd(); int workCount = 50000; ExecutorService executor = Executors.newFixedThreadPool(10); long start = System.currentTimeMillis(); for (int i = 0; i < workCount; i++) { Runnable runnable = new Runnable() { @Override public void run() { counter.increment(); } }; executor.execute(runnable); } // 關閉啟動線程,執行未完成的任務 executor.shutdown(); // 等待所有線程完成任務,完成后才繼續執行下一步 executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); System.out.println("耗時:" + (System.currentTimeMillis() - start) + "ms"); System.out.println("執行結果:count=" + counter.getCount()); } }

為了保證數據的准確性,多線程的情況下需要加上synchronized關鍵字,否則會出現不安全的操作
如果我們換個方式,用AtomicInteger來替換count++,怎么做呢?
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); // 使用AtomicInteger之后,不需要加鎖,也可以實現線程安全。 public void increment() { //獲取當前的值並自增 count.incrementAndGet(); } /** * 獲取當前的值 * @return */ public int getCount() { return count.get(); } //遞減 public void deIncrement(){ count.decrementAndGet(); } /** * 這里模擬一個遞增的任務,遞增目標為50000 */ public static void main(String[] args) throws InterruptedException { final AtomicCounter counter = new AtomicCounter(); int workCount = 50000; ExecutorService executor = Executors.newFixedThreadPool(10); long start = System.currentTimeMillis(); for (int i = 0; i < workCount; i++) { Runnable runnable = new Runnable() { @Override public void run() { counter.increment(); } }; executor.execute(runnable); } // 關閉啟動線程,執行未完成的任務 executor.shutdown(); // 等待所有線程完成任務,完成后才繼續執行下一步 executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); System.out.println("耗時:" + (System.currentTimeMillis() - start) + "ms"); System.out.println("執行結果:count=" + counter.getCount()); } }

AtomicInteger很輕松的實現了線程安全的變量操作
Java從JDK 1.5開始提供了java.util.concurrent.atomic包(以下簡稱Atomic包),這個包中的原子操作類提供了一種用法簡單、性能高效、線程安全地更新一個變量的方式。因為變量的類型有很多種,所以在Atomic包里一共提供了13個類,屬於4種類型的原子更新方式,分別是原子更新基本類型、原子更新數組、原子更新引用和原子更新屬性(字段)。Atomic包里的類基本都是使用Unsafe實現的包裝類。
使用原子的方式更新基本類型,Atomic包提供了以下3個類。
AtomicBoolean:原子更新布爾類型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新長整型。
通過原子的方式更新數組里的某個元素,Atomic包提供了以3類
AtomicIntegerArray:原子更新整型數組里的元素。
AtomicLongArray:原子更新長整型數組里的元素。
AtomicReferenceArray:原子更新引用類型數組里的元素。
AtomicInteger是一個提供原子操作的Integer類,通過線程安全的方式操作加減。
AtomicInteger是在使用非阻塞算法實現並發控制,在一些高並發程序中非常適合,但並不能每一種場景都適合,不同場景要使用使用不同的數值類。
這是由硬件提供原子操作指令實現的,這里面用到了一種並發技術:CAS。在非激烈競爭的情況下,開銷更小,速度更快。
參考:
http://blog.csdn.net/sunxianghuang/article/details/52277370
http://blog.csdn.net/u012734441/article/details/51619751
http://blog.csdn.net/jan_s/article/details/47025095
