最近開始研究並發的問題,今天找了段代碼,是在並發環境共享變量的不安全問題,如下所示:
public class ConcurrentTest { //1000個請求 private static int quest_count = 1000; //同時允許50個請求運行 private static int concurrent_count = 50; private static int count = 0; public static void main(String[] args) throws Exception{ ExecutorService executor = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(concurrent_count);//信號量 final CountDownLatch countDownLatch = new CountDownLatch(quest_count);// for (int i = 0; i < quest_count; i++){ executor.execute(new Runnable() { @Override public void run() { try { semaphore.acquire(); add(); semaphore.release(); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }); } countDownLatch.await(); executor.shutdown(); System.out.printf("count:"+count); } private static void add(){ count++; } }
我創建了一個線程池,是cachedThreadPool,最大支持Integer.MAX_VALUE個線程,具體實現代碼如下:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
之后再詳細說線程池。繼續上面的代碼,我在主線程中通過for循環創建了1000個請求,最多同時允許50個線程運行,每個線程做累加操作,最后打印count結果。
結果如下所示:count:947、count:938、count:876 結果都不是1000,顯而易見的。
原因分析:A線程從主內存中獲取到變量count放置在自己線程私有線程棧中,對count做+1操作 count =1,B線程同時從主存中獲取count(count=0)放置在線程B的棧中,對count做+1操作,之后B和A如果都將count寫回主存,count還是1,我們期望值應該是2。另外++操作在cpu中會拆成三條指令,取值、自增、賦值,並不是原子的,也會有風險。
改造:
方案1:使用AtomicInteger
代碼如下:
public class ConcurrentTest { //1000個請求 private static int quest_count = 1000; //同時允許50個請求運行 private static int concurrent_count = 50; private static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws Exception{ ExecutorService executor = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(concurrent_count);//信號量 final CountDownLatch countDownLatch = new CountDownLatch(quest_count);// for (int i = 0; i < quest_count; i++){ executor.execute(new Runnable() { @Override public void run() { try { semaphore.acquire(); add(); semaphore.release(); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }); } countDownLatch.await(); executor.shutdown(); System.out.printf("count:"+count); } private static void add(){ count.incrementAndGet(); } }
執行結果:count:1000
下一篇文章分析AtomicInteger為什么在多線程下共享變量安全。