如何使多线程(含ExecutorService的10线程)打印完全相同的毫秒级时间?
Hey 你好!这两个问题核心是同一个,我来给你理清楚——不仅明确告诉你可行,还帮你找出CyclicBarrier没成功的原因,再给你靠谱的实现方案~
先给结论:完全可以做到!
要让所有线程打印完全相同的毫秒时间,核心逻辑不是让线程"同时去拿时间",而是让所有线程共享同一个预先确定好的时间值——因为哪怕两个线程的调用间隔只有几纳秒,也可能刚好赶上系统毫秒时钟跳变,导致拿到不同的值。
为什么你的CyclicBarrier尝试失败了?
大概率是你写的逻辑是:让每个线程在CyclicBarrier等待结束后,自己调用System.currentTimeMillis()去拿时间。CyclicBarrier确实能让线程在同一时间点继续执行,但"同一时间点"是线程调度层面的近似,没法保证所有线程在绝对同一纳秒去调用时间方法,只要差一点点,就可能跨了毫秒边界,拿到不同的时间。
正确的实现方式
方式1:最简单的预生成时间法(推荐)
在启动任何线程之前,就在主线程先把时间拿到手,然后每个线程任务直接用这个现成的值就行,完全没并发问题:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SameTimestampPrinter { public static void main(String[] args) { // 主线程提前获取统一的毫秒时间,这时候时间就固定了 long fixedTimestamp = System.currentTimeMillis(); ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { final int threadIndex = i + 1; executor.submit(() -> { // 直接用预先生成的时间,不用自己去拿 System.out.printf("线程%d的打印时间:%d%n", threadIndex, fixedTimestamp); }); } executor.shutdown(); } }
方式2:结合CyclicBarrier实现同步触发(适合需要等待所有线程就绪再输出的场景)
如果你确实需要让所有线程都准备好之后再一起输出(比如做性能测试的同步打点),可以用CyclicBarrier配合线程安全的共享变量,让所有线程等待到同一时刻,再使用预先设置好的时间:
import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; public class BarrierSyncTimestampPrinter { public static void main(String[] args) throws InterruptedException { int threadCount = 10; // 用原子类保证时间变量的线程可见性,避免缓存不一致 AtomicLong sharedTimestamp = new AtomicLong(); // 屏障数量设为线程数+1:因为主线程也要参与等待,确保所有工作线程都就绪后再设置时间 CyclicBarrier barrier = new CyclicBarrier(threadCount + 1, () -> { // 所有线程都到达屏障后,由屏障的回调线程设置统一时间 sharedTimestamp.set(System.currentTimeMillis()); }); ExecutorService executor = Executors.newFixedThreadPool(threadCount); for (int i = 0; i < threadCount; i++) { final int threadIndex = i + 1; executor.submit(() -> { try { // 等待所有工作线程和主线程都到达屏障 barrier.await(); // 此时时间已经被统一设置,直接打印即可 System.out.printf("线程%d的打印时间:%d%n", threadIndex, sharedTimestamp.get()); } catch (Exception e) { // 处理中断或异常 Thread.currentThread().interrupt(); } }); } // 主线程加入屏障等待,触发回调设置时间 barrier.await(); executor.shutdown(); } }
关键注意事项
- ❌ 绝对不要让每个线程单独调用
System.currentTimeMillis():哪怕用了同步工具,也没法保证绝对同时调用,毫秒时间可能在极短时间内变化。 - ✅ 共享时间变量要保证线程可见性:要么用
volatile修饰普通long变量,要么用AtomicLong这类原子类,避免线程读取到缓存的旧值。
内容的提问来源于stack exchange,提问作者Roshni
相关产品推荐
相关产品推荐

