Java多线程累加数字:counter达10时循环无法终止问题求助
解决多线程累加计数器超标的问题
嘿,我明白你现在的困惑——第一次搞多线程很容易踩竞态条件的坑!你的核心问题在于while循环的判断和计数器递增这两步不是原子操作,哪怕你给单个方法加了synchronized,也挡不住多个线程同时“钻空子”。
问题根源分析
你看,当counter等于9的时候,可能有好几个线程同时通过了while(counter < 10)的判断,然后它们都会执行getAndIncrement()。等第一个线程把counter加到10之后,其他已经通过判断的线程还是会继续执行递增操作,结果自然就超过10了。
另外,你的addToSum是实例级别的同步方法,但sum是静态变量——这意味着每个calculator实例的锁是独立的,多个线程调用不同实例的addToSum时,依然会有线程安全问题,只是你没注意到而已。
解决方案
方案1:使用原子类(推荐,更简洁)
Java的java.util.concurrent.atomic包提供了原子操作类,能帮你避免手动同步的麻烦。我们用AtomicInteger替换普通的int计数器,它的getAndIncrement()方法本身就是原子的,而且我们可以把循环判断和取值合并成一个原子操作:
修改后的calculator类:
import java.util.concurrent.atomic.AtomicInteger; public class Calculator implements Runnable { static AtomicInteger counter = new AtomicInteger(0); static int sum = 0; // 静态同步方法,确保sum的累加是线程安全的 private synchronized static void addToSum(int value) { sum += value; } @Override public void run() { int tempVal; // 原子性地获取并递增计数器,同时判断是否小于10 while ((tempVal = counter.getAndIncrement()) < 10) { System.out.println("temp val : " + tempVal); addToSum(tempVal); System.out.println("counter is now : " + (tempVal + 1)); } } }
方案2:手动同步临界区
如果你想手动控制同步逻辑,需要把判断计数器+递增计数器这整个逻辑放到同一个同步块里,确保没有线程能插队:
public class Calculator implements Runnable { static int counter = 0; static int sum = 0; private static void addToSum(int value) { // 锁类对象,因为sum是静态变量 synchronized (Calculator.class) { sum += value; } } @Override public void run() { while (true) { int tempVal; // 锁类对象,确保判断和递增是原子操作 synchronized (Calculator.class) { if (counter >= 10) { break; } tempVal = counter; counter++; System.out.println("counter is now : " + counter); } System.out.println("temp val : " + tempVal); addToSum(tempVal); } } }
优化main方法的等待逻辑
你原来的空循环等待线程结束太浪费资源了,用Thread.join()更优雅:
public static void main(String[] args) { Calculator[] calc = new Calculator[8]; Thread[] thread = new Thread[8]; final long startTime = System.currentTimeMillis(); for (int i = 0; i < 8; i++) { calc[i] = new Calculator(); thread[i] = new Thread(calc[i]); thread[i].start(); } // 等待所有线程结束 for (Thread t : thread) { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } final long endTime = System.currentTimeMillis(); System.out.println(Calculator.sum); System.out.println("Execution time : " + (endTime - startTime)); // 修正了时间计算的顺序 }
关键知识点总结
- 原子性:涉及共享变量的操作,必须确保整个逻辑块是原子的,不能只同步单个步骤。
- 静态变量的同步:静态变量属于类,所以同步时要锁类对象(
Calculator.class),而不是实例对象。 - 避免空循环:用
join()等待线程结束比空轮询更高效也更规范。
内容的提问来源于stack exchange,提问作者Brooke
相关产品推荐
相关产品推荐

