並發編程-多線程共享變量不安全


最近開始研究並發的問題,今天找了段代碼,是在並發環境共享變量的不安全問題,如下所示:

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為什么在多線程下共享變量安全。


免責聲明!

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



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