如何“完全”同时启动两个线程


91

线程应在同一瞬间开始。我知道,如果您这样做thread1.start(),则下次执行之前需要花费几毫秒的时间thread2.start()

可能还是不可能?


2
在GHz速度下,瞬间是很长的时间。
Nikolai Fetissov

30
无需如此激烈地投票。并非所有人都了解与线程相关的不确定性,我们都必须从某个地方开始。
Michael Petrotta

7
不了解下降投票。线程之间的同步是非常普遍的必要。是的,在Java中,不能让它们完全并行执行(顺便说一句,这在其他平台上可能是非常有效的要求),但是有时需要同步它们的动作是非常普遍的。这就是jdk具有执行此操作的类的原因。也许措辞并不准确,但地狱如果他知道,他也不会提出这样的问题..
恩诺盐路

好吧,我想我理解您的所有愤怒。这是在采访中问我的一个问题……可能是个技巧Q。但是,我感到困惑并想确认一下。这就是为什么我问是否有可能的原因。
figaro

2
@javaguy-不是一个“技巧”问题。而是选择一个问题来查看您对一般的多线程编程的了解程度如何……以及在Java案例中。
斯蒂芬·C

Answers:


135

要完全同时(至少尽可能好)启动线程,可以使用CyclicBarrier

// We want to start just 2 threads at the same time, but let's control that 
// timing from the main thread. That's why we have 3 "parties" instead of 2.
final CyclicBarrier gate = new CyclicBarrier(3);

Thread t1 = new Thread(){
    public void run(){
        gate.await();
        //do stuff    
    }};
Thread t2 = new Thread(){
    public void run(){
        gate.await();
        //do stuff    
    }};

t1.start();
t2.start();

// At this point, t1 and t2 are blocking on the gate. 
// Since we gave "3" as the argument, gate is not opened yet.
// Now if we block on the gate from the main thread, it will open
// and all threads will start to do stuff!

gate.await();
System.out.println("all threads started");

不必一定是CyclicBarrier,您也可以使用CountDownLatch甚至是锁。

这仍然不能确保它们在标准JVM上完全同时启动,但是您可以接近它们。当进行性能测试之类的时候,保持非常接近仍然很有用。例如,如果您要尝试测量具有不同数量线程的数据结构的吞吐量,则希望使用这种结构来获得最准确的结果。

在其他平台上,确切地说启动线程可能是一个非常有效的要求。


5
+1好的思维;-)虽然当然它不会使线程在启动恰好在实时同一时间(至少一个单核芯片不上了,甚至在一个多核心芯片不能保证),但我想不出什么办法做得更好。
David Z

这会挂接到特定于环境的等待句柄中吗?
ChaosPandion 2010年

@ChaosPandion:想详细说明吗?
圣诞老人

@Santa-例如,Win32 API提供了不同的原语。一种有用的类型是调用时返回的手动重置事件CreateEventmsdn.microsoft.com/en-us/library/ms686364%28VS.85%29.aspx
ChaosPandion 2010年

1
@Zwei-不管是什么,如果我是Java专家,这就是我要发布的答案。
ChaosPandion 2010年

15

至少在单核计算机上是不可能的。但是,为什么要那样?即使您能够在完全相同的时间启动两个线程,它们的进度也会有所不同,因为调度不在您的控制范围内。

编辑:(响应某些评论)同步多个线程的状态或进度是一个完全有效的要求,并且CyclicBarrier是一个很好的工具。我回答了是否可以同时完全启动多个线程的问题。CyclicBarrier将确保线程在完全处于所需状态时继续执行,但不能保证线程将在完全相同的时间启动或恢复,尽管可能非常接近。这个问题中没有提到同步需求。


1
但是,您可以关闭漂亮的东西。这完全取决于您使用的同步技术,并且当然具有超过1个cpu或内核。
ChaosPandion 2010年

想法是,这是错误的方法,在任何非硬实时环境中都没有必要,不是说它“非常接近”。
Nikolai Fetissov

1
也是不可能的,因为Java线程调度不会给您毫秒级的精度。
斯蒂芬·C

1
@Zwei-在大多数情况下,您可能会在其他闲置的计算机上“关闭”。但是,如果您一直都需要它,则需要在OS中具有实时支持的计算机上使用C之类的语言进行编程。还请考虑一下,JVM可能会在您要启动线程的“瞬间”运行完整的GC。
斯蒂芬·C

1
这是一个完全有效的同步问题。线程需要初始化一些数据,确保没有人经过适当的验证就不会进入关键区域。而且CyclicBerrier包含在java的并发包中这一事实意味着这是一个重要的问题。
丹尼斯·图斯基

14

您可以为此使用CountDownLatch。请在下面找到一个示例。尽管启动了t1和t2,但这些线程一直等待,直到主线程递减锁存器为止。构造函数中提到了所需的倒数次数。倒数锁存器还可以用于等待线程完成执行,以便主线程可以继续执行(相反的情况)。从Java 1.5开始包含该类。

import java.util.concurrent.CountDownLatch;


public class ThreadExample
{
    public static void main(String[] args) 
    {
        CountDownLatch latch = new CountDownLatch(1);
        MyThread t1 = new MyThread(latch);
        MyThread t2 = new MyThread(latch);
        new Thread(t1).start();
        new Thread(t2).start();
        //Do whatever you want
        latch.countDown();          //This will inform all the threads to start
        //Continue to do whatever
    }
}

class MyThread implements Runnable
{
    CountDownLatch latch;
    public MyThread(CountDownLatch latch) 
    {
        this.latch = latch;
    }
    @Override
    public void run() 
    {
        try 
        {
            latch.await();          //The thread keeps waiting till it is informed
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Do the actual thing
    }
}

在代码示例中,该答案可能会受益于更少的样板。
user2418306 '16

6
  1. 据我了解,JVM主要将这些东西委托给操作系统。因此,答案将取决于操作系统。
  2. 在单处理器机器上显然是不可能的。
  3. 关于多处理器机器,它更加复杂。根据同时性的相对性,“如果绝对地讲两个事件在空间上是分开的,那么就不可能在绝对意义上说两个事件是否同时发生。” 无论您的处理器有多近,它们在空间上都是分开的。
    1. 如果您可以接受相对同时性,那么使用其他响应中讨论的技术对其进行仿真可能会更容易。

1
…即使我们假设同时开始,每个线程都有自己的时间轴,所有线程都是并发的,但不一定是并行的,因此,无论有人假设线程的同时性,在下一纳秒内(无论哪个)都将无效。时间轴)…
Holger
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.