如何使多线程数组求和程序产生竞态条件(Race Condition)?
如何修改多线程求和程序以触发竞态条件?
你的代码之所以每次结果都正确,核心原因是每个线程都维护着独立的sum实例变量——每个线程只计算自己负责区间的和,最后再把这些完全隔离的结果相加。全程没有多个线程同时操作同一个共享变量,自然不会出现竞态条件。
要触发竞态条件,必须让多个线程同时操作同一个共享的累加变量。下面是两种简单的修改方案:
方案一:使用静态共享变量
把累加变量改成所有线程共享的静态变量,让所有线程直接修改它:
public class SyncDemo1 { // 所有线程共享的累加变量 private static int totalSum = 0; public static void main(String[] args) { new SyncDemo1().startThread(); } private void startThread() { ExecutorService executor = Executors.newFixedThreadPool(5); // 所有线程操作同一个共享变量,区间划分保持不变 MyThread thread1 = new MyThread(1, 200); MyThread thread2 = new MyThread(201, 400); MyThread thread3 = new MyThread(401, 600); MyThread thread4 = new MyThread(601, 800); MyThread thread5 = new MyThread(801, 1000); executor.execute(thread1); executor.execute(thread2); executor.execute(thread3); executor.execute(thread4); executor.execute(thread5); executor.shutdown(); while (!executor.isTerminated()) {} System.out.println(totalSum); } private static void pause() { try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } private static class MyThread implements Runnable { private int from, to; public MyThread(int from, int to) { this.from = from; this.to = to; } public void run() { for (int i = from; i <= to; i++) { // 非原子操作:读->加->写,多线程下会互相覆盖 totalSum += i; // 可选:加个微睡眠放大竞争概率 // try { Thread.sleep(1); } catch (Exception e) {} } pause(); } } }
方案二:用共享对象封装变量
把共享变量封装到一个对象里,让所有线程操作同一个对象的属性:
public class SyncDemo1 { public static void main(String[] args) { new SyncDemo1().startThread(); } private void startThread() { // 共享的累加器对象,所有线程共用 SumHolder sumHolder = new SumHolder(); ExecutorService executor = Executors.newFixedThreadPool(5); MyThread thread1 = new MyThread(sumHolder, 1, 200); MyThread thread2 = new MyThread(sumHolder, 201, 400); MyThread thread3 = new MyThread(sumHolder, 401, 600); MyThread thread4 = new MyThread(sumHolder, 601, 800); MyThread thread5 = new MyThread(sumHolder, 801, 1000); executor.execute(thread1); executor.execute(thread2); executor.execute(thread3); executor.execute(thread4); executor.execute(thread5); executor.shutdown(); while (!executor.isTerminated()) {} System.out.println(sumHolder.getSum()); } private static void pause() { try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } // 封装共享累加变量的类 private static class SumHolder { private int sum = 0; public void add(int value) { // 同样是非原子操作,会引发竞态条件 sum += value; } public int getSum() { return sum; } } private static class MyThread implements Runnable { private SumHolder sumHolder; private int from, to; public MyThread(SumHolder sumHolder, int from, int to) { this.sumHolder = sumHolder; this.from = from; this.to = to; } public void run() { for (int i = from; i <= to; i++) { sumHolder.add(i); } pause(); } } }
为什么会出现数据不一致?
sum += i这类操作本质上是三个独立的步骤:
- 从主内存读取当前
sum的值到线程的工作内存 - 在工作内存中把读取的值和
i相加 - 把计算结果写回主内存的
sum变量
当多个线程同时执行这三个步骤时,就会出现"覆盖"问题:比如线程A和线程B同时读取到sum=100,线程A计算后写回101,线程B计算后写回102,最终sum的值是102,而正确结果应该是103——这就丢失了一次累加操作,导致最终结果小于预期的500500,且每次运行结果都会不同。
内容的提问来源于stack exchange,提问作者Rex
相关产品推荐
相关产品推荐

