【问题标题】:Not getting expected result when updating AtomicInteger variable in multiple threads在多个线程中更新 AtomicInteger 变量时未获得预期结果
【发布时间】:2019-11-29 08:38:25
【问题描述】:

在这段代码中,我使用了 10 个线程来更新 AtomicInteger 变量。我希望 Counter.getInstance().holder.n 的最终结果是 1000000,但它会打印出像 991591 这样的随机数。 我的代码有什么问题?

public class Test {


    public static void main(String[] args) {

        List<Thread> list = new ArrayList<Thread>();
        for (int i = 0; i < 10; i++) {
            list.add(new Thread() {

                public void run() {
                    for (int i = 0; i < 100000; i++) {
                        Counter.getInstance().holder.n.incrementAndGet();
                    }
                }
            });
        }
        for (Thread thread : list) {
            thread.start();
        }

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Counter.getInstance().holder.n);


    } 
} 

class Counter {
    private static Counter counter;
    Holder holder = new Holder();


    public static Counter getInstance() {
        if (counter == null) {
            counter = new Counter();
        }
        return counter;
    }
    class Holder {
        AtomicInteger n = new AtomicInteger(0);
    } 
}

【问题讨论】:

  • 您的 Counter 单例不是线程安全的,睡眠 10 秒并不能保证所有线程都已完成运行。为您的原子整数使用一个简单的静态变量,并在所有线程上调用 thread.join()(在一个额外的循环中)以等待它们的终止。
  • 你能解释一下为什么单例不是线程安全的吗?以及如何使其线程安全?
  • 因为两个线程可以同时调用getInstance(),都看到counter为null,都创建了一个新的Counter。而且由于没有任何同步,即使一个线程已经将静态计数器设置为非空值,另一个线程也不能保证看到它。使其线程安全:只需使用static final AtomicInteger N = new AtomicInteger(0)

标签: java multithreading concurrency thread-safety atomicinteger


【解决方案1】:

这里有两个主要的并发问题:

  1. 您不必等待每个Thread 正确完成工作。有多种方法可以实现,最简单的是使用Thread.join()
  2. 您的单例实现似乎不正确。我想你打算用一个内部类来实现它。看来这个answer 可以帮助理解这里发生了什么。

这是看起来或多或少正确的实现。

class Test {
    public static void main(String[] args) throws InterruptedException {

        List<Thread> list = new ArrayList<Thread>();
        for (int i = 0; i < 10; i++) {
            list.add(new Thread() {

                public void run() {
                    for (int i = 0; i < 100000; i++) {
                        Counter.getInstance().n.incrementAndGet();
                    }
                }
            });
        }
        for (Thread thread : list) {
            thread.start();
        }

        for (Thread thread : list) {
            thread.join();
        }

        System.out.println(Counter.getInstance().n);
    }
}

class Counter {
    public AtomicInteger n = new AtomicInteger(0);

    public static Counter getInstance() {
        return Holder.instance;
    }
    private static class Holder {
        private static final Counter instance = new Counter();
    }
}

你也可以使用CountDownLatch 之类的东西。例如:

final int count = 10;
CountDownLatch latch = new CountDownLatch(count);
List<Thread> list = new ArrayList<Thread>();
for (int i = 0; i < count; i++) {
    list.add(new Thread() {

        public void run() {
            for (int i = 0; i < 100000; i++) {
                Counter.getInstance().n.incrementAndGet();
            }
            latch.countDown();
        }
    });
}
for (Thread thread : list) {
    thread.start();
}

latch.await();

System.out.println(Counter.getInstance().n);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-16
    • 1970-01-01
    • 1970-01-01
    • 2017-04-14
    • 1970-01-01
    • 2017-08-23
    • 1970-01-01
    • 2016-10-28
    相关资源
    最近更新 更多