从其他线程更新 AtomicInteger
Update AtomicInteger from other thread
我有一个 class 可以在各自的线程上创建许多新对象,我想在线程中保持 运行 计数。我想要一个 AtomicInteger
但它没有达到我的预期,而是得到了一个较小的版本。我假设这是一个竞争条件错误 - 但我不完全确定。
A 创建了这个测试示例,它重现了我想做的事情。
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 时。如何跨线程递增?
提前致谢。
是的,存在数据竞争。竞争发生在调用 doThing.getTally()
的主线程和启动的 "worker" 线程之间。看起来每次你的主线程都能够在工作人员有机会进入其 for
循环之前可靠地从每个工作人员那里获得 "tally" 。它甚至可能在工作人员调用其 run()
方法之前发生。
您的主线程需要 join
工作人员:
- 创建并启动十个工作线程,并将每一个添加到
List<Thread>
。
- 然后在单独的循环中,为列表中的每个线程
t
调用 t.join()
。 t.join()
函数等待线程 t
完成其工作。
- 最后,统计并相加。
在您的示例代码中,total 只能从主线程访问。使其成为 Atomic 不会对结果产生任何影响。您应该将 Atomic 值传递给您的线程并增加其中的值。或者使用LongAdder(自增法)。
在主线程中打印 Atomic 值之前,您必须等待所有线程完成。
如果你想使用低级阻塞,你可以使用 CyclicBarrier 让主线程等待所有线程。
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;
}
}
试试这个:
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
实例来保持 up-to-date 计数,并且您可以通过等待确保总数正确让所有线程在获得计数之前通过 join()
ing 完成它们的计数。
我有一个 class 可以在各自的线程上创建许多新对象,我想在线程中保持 运行 计数。我想要一个 AtomicInteger
但它没有达到我的预期,而是得到了一个较小的版本。我假设这是一个竞争条件错误 - 但我不完全确定。
A 创建了这个测试示例,它重现了我想做的事情。
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 时。如何跨线程递增?
提前致谢。
是的,存在数据竞争。竞争发生在调用 doThing.getTally()
的主线程和启动的 "worker" 线程之间。看起来每次你的主线程都能够在工作人员有机会进入其 for
循环之前可靠地从每个工作人员那里获得 "tally" 。它甚至可能在工作人员调用其 run()
方法之前发生。
您的主线程需要 join
工作人员:
- 创建并启动十个工作线程,并将每一个添加到
List<Thread>
。 - 然后在单独的循环中,为列表中的每个线程
t
调用t.join()
。t.join()
函数等待线程t
完成其工作。 - 最后,统计并相加。
在您的示例代码中,total 只能从主线程访问。使其成为 Atomic 不会对结果产生任何影响。您应该将 Atomic 值传递给您的线程并增加其中的值。或者使用LongAdder(自增法)。
在主线程中打印 Atomic 值之前,您必须等待所有线程完成。
如果你想使用低级阻塞,你可以使用 CyclicBarrier 让主线程等待所有线程。
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;
}
}
试试这个:
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
实例来保持 up-to-date 计数,并且您可以通过等待确保总数正确让所有线程在获得计数之前通过 join()
ing 完成它们的计数。