从其他线程更新AtomicInteger

时间:2018-02-22 19:58:54

标签: java multithreading atomicinteger

我有一个类,它们在自己的线程上创建了许多新对象,我希望在线程中保持运行计数。我希望有一个AtomicInteger,但它没有达到我的预期,而只是获得了一个较小的版本。我假设这是一个竞争条件错误 - 但我并不完全确定。

创建了这个测试示例,重新创建了我想要做的事情。

public class Test {

    public static void main(String args[]) {
        AtomicInteger total = new AtomicInteger(0);
        for (int i = 0; i < 10; i++) {

            DoThing doThing = new DoThing();

            Thread thread = new Thread(doThing);
            thread.start();
            total.addAndGet(doThing.getTally());
        }

        System.out.println(total.get());
    }
}

class DoThing implements Runnable {

    int tally = 0;
    @Override
    public void run() {

        for(int i = 0; i< 100; i++) {
            tally++;
        }

        System.out.println("Tally is " + tally);

    }

    public int getTally() {
        return tally;
    }
}

然而,这输出:

Tally is 100
Tally is 100
Tally is 100
Tally is 100
Tally is 100
Tally is 100
Tally is 100
Tally is 100
0
Tally is 100
Tally is 100

当我希望最终输出为1000时。如何在线程之间递增?

提前致谢。

4 个答案:

答案 0 :(得分:2)

是的,有数据竞争。比赛是在调用doThing.getTally()的主线程和启动的“worker”线程之间进行的。看起来每次主线程能够在工作人员有机会进入其for循环之前能够可靠地从每个工作人员那里获得“计数”。甚至在工作者调用其run()方法之前就可能发生这种情况。

您的主要线程需要join工人:

  • 创建并启动十个工作线程,并将每个线程添加到List<Thread>
  • 然后在单独的循环中,为列表中的每个线程t.join()调用tt.join()函数等待线程t完成其工作。
  • 最后,拿出标签并加起来。

答案 1 :(得分:2)

在您的示例代码中,只能从主线程访问total。使其成为Atomic不会对结果产生任何影响。您应该将原子值传递给线程并增加其中的值。或者使用LongAdder(增量法) 在主线程中打印原子值之前,必须等待所有线程完成 如果要使用低级别阻塞,可以使用CyclicBarrier使主线程等待所有线程。

答案 2 :(得分:2)

试试这个:

public static void main(String args[]) {
    AtomicInteger tally = new AtomicInteger(0);
    List<Thread> threadList = new ArrayList<Thread>();
    for (int i = 0; i < 10; i++) {
        Thread t = new Thread(new DoThing(tally));
        t.start();
        threadList.add(t);
    }
    for (Thread t : threadList) {
        try { t.join(); } catch (Exception e){}
    }
    System.out.println("Total tally: " + tally.get());
}

public static class DoThing implements Runnable {
    private static final Random rand = new Random();
    private final AtomicInteger tally;

    public DoThing(AtomicInteger tally) {
        this.tally = tally;
    }

    @Override public void run() {
        for (int i = 0; i < 100; i++) {
            int currTally  = tally.incrementAndGet();
            System.out.println("Thread " + Thread.currentThread().getName() + ": " + currTally);
            // Random sleep to show that your threads are properly concurrently incrementing
            try { Thread.sleep(rand.nextInt(10)); } catch (Exception e) {}
        }
    }
}

问题的根源在于您误解了如何使用AtomicInteger,您正在将其视为普通int并且根本不会同时访问它。

同样getTally()是竞争条件,直到您通过调用Thread.join()确保线程已完成。

因此,您可以通过让线程中的所有Runnable更新相同的AtomicInteger实例来保持最新的计数,并且您可以通过等待所有实例确保您拥有正确的总数在获得统计数据之前join()完成他们的标记的线程。

答案 3 :(得分:1)

CountDownLatch latch = new CountDownLatch(10);
List<DoThing> things = new ArrayList();
AtomicInteger total = new AtomicInteger(0);
    for (int i = 0; i < 10; i++) {

        DoThing doThing = new DoThing(latch);
        things.add(doThing);
        Thread thread = new Thread(doThing);
        thread.start();
        total.addAndGet(doThing.getTally());
    }
// Wait till all the threads are done.
// Each thread counts the latch down
latch.await()
int total = 0;

// Calculate sum after all the threads are done.
for (DoThing thing: things) {
    total += thing.getTally();
}
System.out.println(total);



class DoThing implements Runnable {

    private CountDownLatch latch;

    public DoThing(CountDownLatch latch) {
        this.latch = latch;
    }

    int tally = 0;
    @Override
    public void run() {

        for(int i = 0; i< 100; i++) {
            tally++;
        }
        latch.countDown();
        System.out.println("Tally is " + tally);

    }

    public int getTally() {
        return tally;
    }
}