Java多线程赛跑程序移除System.out.println后逻辑异常排查
问题根因
你的代码存在4个核心并发错误,删掉System.out.println后逻辑错乱只是刚好戳破了之前的假正常现象:
- 锁完全不生效:
ThreadThings里的lock是实例变量,每个选手线程都持有自己独立的锁对象,加锁操作只锁当前线程自己,根本没法和其他线程形成互斥。Main方法里定义的锁和子线程任务完全无关,主线程加锁解锁对子线程执行没有任何影响,属于无效代码。 - 共享变量无可见性保证:静态标记
winner是所有线程共享的状态,但你既没有用volatile修饰,也没有在有效锁的保护下读写,一个线程修改winner=true之后,其他线程可能一直从自己的工作缓存里读到旧的false值。之所以留着println的时候看起来正常,是因为System.out.println底层实现自带synchronized同步,调用时会触发内存屏障强制刷新线程缓存,歪打正着保证了变量可见性,删掉之后这个偶然的保障消失,并发问题直接暴露。 - 锁的粒度完全错误:你把整段跑步逻辑都包在锁里,就算锁是有效的,也会变成一个线程拿锁之后直接跑完100米全程才释放锁,其他线程全程等着,完全是串行执行,根本不是多线程赛跑的效果。
- 无效的同步修饰:你给run方法加的
synchronized关键字锁的是当前ThreadThings实例,和你定义的实例锁一样,没法跨实例同步多个线程,没有任何实际作用。
修正实现
修正逻辑遵循三个原则:所有线程共用同一把锁、只在判断/更新冠军状态的临界区加锁、锁释放必须放在finally块避免死锁,同时给共享状态加可见性保证。
修正后的ThreadThings类:
package com.company; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ThreadThings implements Runnable { // 全局共享锁,所有参赛线程共用 private static final Lock RACE_LOCK = new ReentrantLock(); // 全局冠军标记,volatile保证多线程间的修改可见 private static volatile boolean hasWinner = false; private final String runnerName; private int position = 0; public ThreadThings(String runnerName) { this.runnerName = runnerName; } @Override public void run() { long startTime = System.currentTimeMillis(); for (int i = 1; i <= 100; i++) { position = i; // 模拟跑步耗时,可替换为随机休眠模拟真实速度差异 try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } // 冲线时进入临界区判定冠军 if (position == 100) { RACE_LOCK.lock(); try { if (!hasWinner) { hasWinner = true; System.out.println(runnerName + " wins"); System.out.println("Congrats " + runnerName + " you are the winner"); } } finally { RACE_LOCK.unlock(); } } } long totalTime = System.currentTimeMillis() - startTime; System.out.println(runnerName + " finished, total time: " + totalTime + "ms"); } }
修正后的Main类,删掉无效的锁逻辑:
package com.company; public class Main { public static void main(String[] args) { new Thread(new ThreadThings("Abby")).start(); new Thread(new ThreadThings("Brandon")).start(); new Thread(new ThreadThings("Casey")).start(); new Thread(new ThreadThings("David")).start(); new Thread(new ThreadThings("Eddie")).start(); new Thread(new ThreadThings("Frank")).start(); new Thread(new ThreadThings("Gabby")).start(); new Thread(new ThreadThings("Hannah")).start(); new Thread(new ThreadThings("Indie")).start(); new Thread(new ThreadThings("Jasmine")).start(); } }
补充说明:原代码里的耗时单位标注错误,
System.currentTimeMillis()返回的时间戳单位是毫秒,不是分钟。如果要模拟更真实的赛跑效果,可以把固定10ms的休眠替换为Thread.sleep((long)(Math.random() * 20)),让每个选手的每米耗时随机。
内容的提问来源于stack exchange,提问作者Rhion Kendall
相关产品推荐
相关产品推荐

