Como iniciar dois tópicos "exatamente" ao mesmo tempo

91

Os tópicos devem começar na mesma fração de segundo. Eu entendo, se você fizer thread1.start()isso, vai demorar alguns milissegundos antes da próxima execução dethread2.start() .

É mesmo possível ou impossível?

figaro
fonte
2
Fração de segundo é um tempo muito longo em velocidades GHz.
Nikolai Fetissov
30
Não há necessidade de downvote tão intensamente. Nem todo mundo entende o não-determinismo relacionado ao threading, e todos nós temos que começar de algum lugar.
Michael Petrotta
7
Não entendo os votos negativos. A sincronização entre threads é uma necessidade muito comum. Sim, em java você não pode executá-los exatamente em paralelo (o que, em outra plataforma, pode ser um requisito muito válido btw), mas de vez em quando é muito comum que você precise sincronizar sua ação. É por isso que o jdk tem aulas para fazer isso. Talvez as palavras não fossem precisas, mas se ele soubesse disso, não teria feito a pergunta ..
Enno Shioji
bem, acho que entendo toda a sua raiva. Foi uma pergunta feita a mim em uma entrevista ... provavelmente foi um truque Q. Mas, fiquei confuso e queria confirmar. é por isso que perguntei se era mesmo possível.
figaro
2
@javaguy - não é uma pergunta "capciosa". Em vez disso, uma pergunta escolhida para ver o quão bem você realmente entende a programação multi-threaded em geral ... também no caso do Java.
Stephen C

Respostas:

135

Para iniciar os threads exatamente ao mesmo tempo (pelo menos da melhor forma possível), você pode usar um 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");

Não precisa ser um CyclicBarrier, você também pode usar um CountDownLatchou até mesmo um cadeado.

Isso ainda não pode garantir que eles sejam iniciados exatamente ao mesmo tempo em JVMs padrão, mas você pode chegar bem perto. Chegar bem perto ainda é útil quando você faz, por exemplo, testes de desempenho. Por exemplo, se você está tentando medir o rendimento de uma estrutura de dados com um número diferente de threads atingindo-a, você deseja usar esse tipo de construção para obter o resultado mais preciso possível.

Em outras plataformas, iniciar threads exatamente pode ser um requisito muito válido btw.

Enno Shioji
fonte
5
+1 bom pensamento ;-) Embora seja claro que isso não faça com que os threads comecem exatamente ao mesmo tempo em tempo real (pelo menos não em um chip de núcleo único, e não garantido mesmo em um chip de vários núcleos), mas Não consigo pensar em nenhuma maneira de fazer melhor.
David Z
Será que este gancho em alças de espera específicas do ambiente?
ChaosPandion
@ChaosPandion: Importa-se de elaborar?
Santa
@Santa - A API Win32, por exemplo, oferece diferentes primitivas. Um tipo útil é o evento de reinicialização manual retornado quando você chama CreateEvent. msdn.microsoft.com/en-us/library/ms686364%28VS.85%29.aspx
ChaosPandion
1
@Zwei - Bem, seja o que for, esta é a resposta que eu teria postado se fosse um guru de Java.
ChaosPandion
15

Não é possível, pelo menos em um computador de núcleo único. Mas por que você quer isso? Mesmo se você pudesse iniciar dois threads exatamente no mesmo segundo, eles progrediriam de forma diferente porque o agendamento não está sob seu controle.

Editar: (em resposta a alguns dos comentários) É um requisito perfeitamente válido sincronizar o estado ou o progresso de vários threads e CyclicBarrieré uma ótima ferramenta. Eu respondi à pergunta se é possível iniciar vários threads exatamente ao mesmo tempo . CyclicBarrierirá garantir que os threads continuem quando estiverem exatamente no estado desejado, mas não garante que eles serão iniciados ou retomados exatamente ao mesmo tempo, embora possa ser bem próximo. Não há menção das necessidades de sincronização na pergunta.

samitgaur
fonte
1
Você pode chegar bem perto embora. Tudo depende das técnicas de sincronização que você usa e, claro, de ter mais de 1 cpu ou núcleo.
ChaosPandion
A ideia é que essa abordagem é equivocada e não deve ser necessária em nenhum ambiente de tempo real não rígido, não que seja "muito próximo".
Nikolai Fetissov
1
É também impossível porque thread Java programação não lhe dá a precisão de milissegundos.
Stephen C
1
@Zwei - você provavelmente pode chegar "muito perto" na maioria das vezes em uma máquina ociosa. Mas se você precisa disso o tempo todo, precisa programar em uma linguagem como C, em uma máquina com suporte rígido em tempo real no sistema operacional. Considere também que a JVM pode estar executando um GC completo na "fração de segundo" em que você deseja iniciar seus encadeamentos.
Stephen C
1
É um problema de sincronização perfeitamente válido. Threads precisam inicializar alguns dados, certifique-se de que ninguém entre em uma região crítica sem a validação adequada. E o fato de CyclicBerrier estar incluído no pacote simultâneo do Java significa que este é um problema importante.
Denis Tulskiy
14

Você pode usar um CountDownLatch para isso. Veja abaixo um exemplo. Embora t1 e t2 sejam iniciados, esses encadeamentos continuam esperando até que o encadeamento principal faça a contagem regressiva da trava. O número de contagens regressivas necessárias é mencionado no construtor. A trava de contagem regressiva também pode ser usada para esperar que os encadeamentos concluam a execução para que o encadeamento principal possa prosseguir (o caso inverso). Esta classe foi incluída desde 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
    }
}
aNish
fonte
Essa resposta pode se beneficiar de menos clichês no exemplo de código.
user2418306
6
  1. Pelo que entendi, a JVM principalmente delega essas coisas ao sistema operacional. Portanto, a resposta será específica do sistema operacional.
  2. É claramente impossível em máquinas com um único processador.
  3. É mais complicado no que diz respeito a uma máquina com vários processadores. De acordo com a relatividade da simultaneidade , “é impossível dizer em um sentido absoluto se dois eventos ocorrem ao mesmo tempo se esses eventos estão separados no espaço”. Não importa o quão próximos estejam seus processadores, eles estão separados no espaço.
    1. Se você pode aceitar a simultaneidade relativa, então provavelmente é mais fácil apenas simulá-la usando as técnicas discutidas em outras respostas.
emory
fonte
1
... e mesmo se assumirmos um início simultâneo, cada thread tem sua própria linha do tempo, todos eles sendo simultâneos , mas não necessariamente paralelos, então tudo o que alguém presumir sobre a simultaneidade de threads pode (será) ser anulado no próximo nanossegundo (de qualquer cronograma) ...
Holger