Java多线程模拟跑步比赛 如何实现选手排名及总耗时统计
问题原因
- 总耗时统计错误:主线程调用
start()启动子线程后不会阻塞等待子线程执行完成,会直接执行后续计算总耗时的逻辑,拿到的时间仅为启动线程的耗时,和实际比赛时长不符。 - 排名无法正确分配:多线程并发执行时,普通int变量的更新不是原子操作,存在线程安全问题,无法保证先跑完的线程优先拿到递增的排名。
解决方案
步骤1:修改Runner类,添加线程安全的排名计数器
使用AtomicInteger作为全局共享的排名计数器,保证排名分配的原子性,避免并发冲突:
import java.util.concurrent.atomic.AtomicInteger; public class Runner extends Thread { // 全局共享的原子排名计数器,初始值为1 private static final AtomicInteger rankCounter = new AtomicInteger(1); // 原有成员变量:lane、bib、country、lastName、firstName、reaction等保持不变 // 修改后的raceStart方法 private void raceStart() throws InterruptedException { long start = System.currentTimeMillis(); Thread.sleep((long) (reaction * 1000)); int track = 100; int playerLocation = 0; Random randomDelay = new Random(); double delay = 90 + randomDelay.nextInt(16); for(int i = 0; i <= track; i++) { if(playerLocation == track) { long finish = System.currentTimeMillis(); double totalTime = (double)(finish - start) / 1000; // 原子获取当前排名,计数器自动+1 int currentRank = rankCounter.getAndIncrement(); System.out.format("%d %d %d %-12s %-12s %-25s %.3f %.3f%n" , currentRank, lane, bib, country, lastName, firstName, reaction, totalTime); } playerLocation++; Thread.sleep((long) delay); } } // 原有构造方法等逻辑保持不变 }
步骤2:修改主类逻辑,等待所有线程执行完成后统计总耗时
启动所有选手线程后,调用join()方法阻塞主线程,直到所有选手线程执行完毕再计算总耗时:
// 修改后的run方法,返回所有选手线程实例 private List<Runner> run() { Runner usainBolt = new Runner(1, 6, 2612, "JAM", "Bolt", "Usain", 0.155); Runner justinGatlin = new Runner(2, 4, 3069, "USA", "GATLIN", "Justin", 0.152); Runner andreDeGrasse = new Runner(3, 7, 2196, "CAN", "DE GRASSE", "Andre", 0.141); Runner yohanBlake = new Runner(4, 9, 2611, "JAM", "BLAKE", "Yohan", 0.145); Runner akaniSimbine = new Runner(5, 3, 2909, "RSA", "SIMBINE", "Akani", 0.128); Runner benYoussefMeite = new Runner(6, 8, 2245, "CIV", "MEITE", "Ben Youssef", 0.156); Runner jimmyVicaut = new Runner(7, 5, 2434, "FRA", "VICAUT", "Jimmy", 0.140); Runner trayvonBromell = new Runner(8, 2, 3054, "USA", "BROMWELL", "Trayvon", 0.135); List<Runner> runners = Arrays.asList(usainBolt, justinGatlin, andreDeGrasse, yohanBlake, akaniSimbine, benYoussefMeite, jimmyVicaut, trayvonBromell); for (Runner r : runners) { r.start(); } return runners; } // 总耗时统计逻辑 public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); List<Runner> runners = new MainClass().run(); // 等待所有选手线程执行完成 for (Runner r : runners) { r.join(); } long finish = System.currentTimeMillis(); double totalTime = (double)(finish - start) / 1000; System.out.println("比赛总耗时:" + totalTime + "秒"); }
可选优化
如果要避免线程启动顺序带来的时间误差,让所有选手完全同时起跑,可以使用CountDownLatch作为起跑信号,所有选手线程等待主线程统一发令后再开始执行。
内容的提问来源于stack exchange,提问作者Dalvir Singh
相关产品推荐
相关产品推荐

