Как запустить два потока «точно» одновременно

92

Потоки должны начинаться в одну и ту же долю секунды. Я понимаю, что если вы это сделаете thread1.start(), то до следующего выполнения thread2.start().

Это вообще возможно или невозможно?

Фигаро
источник
2
Доля секунды - это очень много времени на скоростях ГГц.
Николай Фетисов
32
Нет необходимости так сильно понижать голос. Не все понимают недетерминизм, связанный с потоками, и всем нам нужно с чего-то начинать.
Майкл Петротта
7
Не понимаю голосов против. Синхронизация между потоками - очень распространенная необходимость. Да, в java вы не можете заставить их выполняться точно параллельно (что на другой платформе может быть очень допустимым требованием, кстати), но время от времени очень часто вам нужно синхронизировать их действия. Вот почему в jdk есть классы для этого. Может быть, формулировка была неточной, но, черт возьми, если бы он знал это, он бы не задал этот вопрос ..
Энно Сиодзи
ну, думаю, я понимаю весь твой гнев. Это был вопрос, который мне задали в интервью ... возможно, это была уловка. В. Но я просто запутался и хотел подтвердить это. поэтому я спросил, возможно ли это вообще.
figaro
2
@javaguy - вопрос не с подвохом. Скорее вопрос, выбранный для того, чтобы увидеть, насколько хорошо вы действительно понимаете многопоточное программирование в целом ... а также в случае Java.
Stephen C

Ответы:

136

Чтобы запускать потоки в одно и то же время (по крайней мере, насколько это возможно), вы можете использовать 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замок или даже замок.

Это до сих пор не может убедиться , что они начали именно в то же время на стандартных виртуальных машинах, но вы можете получить довольно близко. Подобраться довольно близко по-прежнему полезно, когда вы, например, проводите тесты производительности. Например, если вы пытаетесь измерить пропускную способность структуры данных при разном количестве обращающихся к ней потоков, вы хотите использовать такую ​​конструкцию, чтобы получить наиболее точный результат.

На других платформах, начиная потоки точно могут быть очень действительное требование кстати.

Энно Сиодзи
источник
5
+1 хорошая мысль ;-) Хотя, конечно , это не делает нити начинают в точности то же время в режиме реального времени (по крайней мере , не на одноядерном чипе, а не гарантируется даже на многоядерных чипов), но Я не могу придумать, как сделать лучше.
David Z
Будет ли это подключаться к дескрипторам ожидания для конкретной среды?
ChaosPandion
@ChaosPandion: Хотите уточнить?
Санта
@Santa - API Win32, например, предлагает различные примитивы. Один из полезных типов - событие ручного сброса, возвращаемое при вызове CreateEvent. msdn.microsoft.com/en-us/library/ms686364%28VS.85%29.aspx
ChaosPandion
1
@Zwei - Что ж, это ответ, который я бы опубликовал, будь я гуру Java.
ChaosPandion
15

Это невозможно, по крайней мере, на одноядерном компьютере. Но зачем тебе это нужно? Даже если вы смогли запустить два потока точно в одну и ту же секунду, они будут выполняться по-разному, потому что планирование не находится в вашей власти.

Изменить: (в ответ на некоторые комментарии) Это совершенно правильное требование для синхронизации состояния или хода выполнения нескольких потоков, и CyclicBarrierэто отличный инструмент. Я ответил на вопрос, можно ли запускать несколько потоков в одно и то же время . CyclicBarrierбудет гарантировать , что нити проследовать , когда они находятся точно в нужном состоянии , но это не гарантирует , что они будут начинаться или резюме в точно то же самое время, хотя это может быть довольно близко. В вопросе нет упоминания о необходимости синхронизации.

самитгаур
источник
1
Хотя вы можете подобраться чертовски близко. Все зависит от используемых вами методов синхронизации и, конечно, от наличия более 1 процессора или ядра.
ChaosPandion
Идея заключается в том, что это ошибочный подход и не должен быть необходим в любой среде, не связанной с жестким режимом реального времени, а не потому, что он «довольно близок».
Николай Фетисов
1
Это также невозможно, потому что планирование потоков Java не дает точности до миллисекунд.
Stephen C
1
@Zwei - вы, вероятно, можете получить "чертовски близко" большую часть времени на простаивающей машине. Но если вам это нужно постоянно, вам нужно программировать на таком языке, как C, на машине с жесткой поддержкой реального времени в ОС. Учтите также, что JVM может запускать полный сборщик мусора в «долю секунды», в которую вы хотите запустить свои потоки.
Stephen C
1
Это вполне актуальная проблема синхронизации. Потокам необходимо инициализировать некоторые данные, убедитесь, что никто не входит в критическую область без надлежащей проверки. И тот факт, что CyclicBerrier включен в пакет javas concurrent, означает, что это важная проблема.
Денис Тульский
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 02
6
  1. Насколько я понимаю, JVM в основном делегирует все это операционной системе. Так что ответ будет зависеть от ОС.
  2. На однопроцессорных машинах это явно невозможно.
  3. С многопроцессорной машиной все сложнее. Согласно теории относительности одновременности , «невозможно сказать в абсолютном смысле, происходят ли два события одновременно, если эти события разделены в пространстве». Независимо от того, насколько близко находятся ваши процессоры, они разделены пространством.
    1. Если вы можете принять относительную одновременность, то, вероятно, будет проще просто смоделировать ее, используя методы, обсуждаемые в других ответах.
Эмори
источник
1
... и даже если мы предполагаем одновременный запуск, каждый поток имеет свою собственную временную шкалу, причем все они параллельны , но не обязательно параллельны, поэтому все, что кто-то предполагает об одновременности потоков, может (будет) быть недействительным в следующую наносекунду (в зависимости от того, что timeline)…
Holger