Estou um pouco confuso sobre o uso do yield()
método em Java, especificamente no código de exemplo abaixo. Eu também li que yield () é 'usado para prevenir a execução de um thread'.
Minhas perguntas são:
Eu acredito que o código abaixo resulta na mesma saída tanto ao usá-lo
yield()
quanto ao não usá-lo. Isso está correto?Quais são, de fato, os principais usos de
yield()
?De que forma é
yield()
diferente dos métodosjoin()
einterrupt()
?
O exemplo de código:
public class MyRunnable implements Runnable {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
for(int i=0; i<5; i++) {
System.out.println("Inside main");
}
}
public void run() {
for(int i=0; i<5; i++) {
System.out.println("Inside run");
Thread.yield();
}
}
}
Eu obtenho a mesma saída usando o código acima com e sem usar yield()
:
Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run
yield()
e não. quando você tem um i grande em vez de 5, pode ver o efeito doyield()
método.Respostas:
Fonte: http://www.javamex.com/tutorials/threads/yield.shtml
fonte
Vejo que a pergunta foi reativada com generosidade, perguntando agora quais são os usos práticos
yield
. Vou dar um exemplo de minha experiência.Como sabemos,
yield
força o thread de chamada a desistir do processador no qual está sendo executado para que outro thread possa ser agendado para ser executado. Isso é útil quando o encadeamento atual concluiu seu trabalho por enquanto, mas deseja retornar rapidamente para o início da fila e verificar se alguma condição mudou. Como isso é diferente de uma variável de condição?yield
permite que o thread retorne muito mais rápido ao estado de execução. Ao esperar por uma variável de condição, o encadeamento é suspenso e precisa aguardar um encadeamento diferente para sinalizar que deve continuar.yield
basicamente diz "permitir a execução de um thread diferente, mas permita-me voltar ao trabalho logo, pois espero que algo mude em meu estado muito rapidamente". Isso sugere uma rotação ocupada, em que uma condição pode mudar rapidamente, mas suspender o encadeamento incorreria em um grande impacto no desempenho.Mas chega de tagarelice, aqui está um exemplo concreto: o padrão paralelo da frente de onda. Uma instância básica desse problema é calcular as "ilhas" individuais de 1s em uma matriz bidimensional preenchida com 0s e 1s. Uma "ilha" é um grupo de células adjacentes umas às outras, vertical ou horizontalmente:
Aqui temos duas ilhas de 1s: superior esquerdo e inferior direito.
Uma solução simples é fazer uma primeira passagem por toda a matriz e substituir os valores 1 por um contador incremental de forma que no final cada 1 seja substituído por seu número de sequência na ordem principal da linha:
Na próxima etapa, cada valor é substituído pelo mínimo entre ele e os valores de seus vizinhos:
Agora podemos facilmente determinar que temos duas ilhas.
A parte que queremos executar em paralelo é a etapa em que calculamos os mínimos. Sem entrar em muitos detalhes, cada thread obtém linhas de uma maneira intercalada e depende dos valores calculados pelo thread que processa a linha acima. Assim, cada encadeamento precisa ficar ligeiramente para trás do encadeamento que processa a linha anterior, mas também deve se manter dentro de um tempo razoável. Mais detalhes e uma implementação são apresentados por mim neste documento . Observe o uso de
sleep(0)
que é mais ou menos equivalente a C deyield
.Nesse caso,
yield
foi usado para forçar cada thread a pausar, mas como o thread que processa a linha adjacente avançaria muito rapidamente nesse meio tempo, uma variável de condição seria uma escolha desastrosa.Como você pode ver,
yield
é uma otimização bastante refinada. Usá-lo no lugar errado, por exemplo, aguardar uma condição que muda raramente, causará uso excessivo da CPU.Desculpe pela longa tagarelice, espero ter sido claro.
fonte
yield
quando a condição não é satisfeita para dar aos outros threads chance de prosseguir com o cálculo, em vez de usar mais primitivos de sincronização de nível, certo?yield
.Sobre as diferenças entre
yield()
,interrupt()
ejoin()
- em geral, não apenas em Java:Para Java especificamente, consulte
Entrando:
Como usar Thread.join? (aqui no StackOverflow)
Quando juntar tópicos?
Produzindo:
Interrompendo:
Thread.interrupt () é ruim? (aqui no StackOverflow)
fonte
wait()
não é uma junção, é sobre um bloqueio no objeto que o thread de chamada está tentando adquirir - ele espera até que o bloqueio seja liberado por outros e tenha sido adquirido pelo thread. Ajustou minha resposta de acordo.Primeiro, a descrição real é
Agora, é muito provável que sua thread principal execute o loop cinco vezes antes que o
run
método da nova thread seja executado, portanto, todas as chamadas deyield
acontecerão somente depois que o loop na thread principal for executado.join
irá parar o thread atual até que o thread que está sendo chamado termine de serjoin()
executado.interrupt
interromperá a thread em que está sendo chamada, causando InterruptedException .yield
permite uma mudança de contexto para outras threads, de forma que esta thread não consuma todo o uso da CPU do processo.fonte
SwitchToThread()
chamar é melhor do que Sleep (0) e isso deve ser um bug em Java :)Não há diferença prática
Thread.yield()
entre as versões do Java desde 6 a 9.TL; DR;
Conclusões baseadas no código-fonte do OpenJDK ( http://hg.openjdk.java.net/ ).
Se não levar em conta o suporte de HotSpot de sondas USDT (as informações de rastreamento do sistema estão descritas no guia dtrace ) e a propriedade JVM
ConvertYieldToSleep
, o código-fonte deyield()
é quase o mesmo. Veja a explicação abaixo.Java 9 :
Thread.yield()
chama o método específico do sistema operacionalos::naked_yield()
:No Linux:
No Windows:
Java 8 e anterior:
Thread.yield()
chama o método específico do sistema operacionalos::yield()
:No Linux:
No Windows:
Como você pode ver,
Thread.yeald()
no Linux é idêntico para todas as versões do Java.Vamos ver o Windows
os::NakedYield()
do JDK 8:A diferença entre Java 9 e Java 8 na verificação adicional da existência do
SwitchToThread()
método da API Win32 . O mesmo código está presente no Java 6.O código-fonte do
os::NakedYield()
JDK 7 é um pouco diferente, mas tem o mesmo comportamento:A verificação adicional foi descartada porque o
SwitchToThread()
método está disponível desde o Windows XP e Windows Server 2003 (consulte as notas do msdn ).fonte
Yield sugere à CPU que você pode interromper a thread atual e começar a executar threads com prioridade mais alta. Em outras palavras, atribuir um valor de baixa prioridade ao segmento atual para deixar espaço para segmentos mais críticos.
NÃO, os dois produzirão resultados diferentes. Sem yield (), uma vez que a thread obtenha o controle, ela executará o loop 'Inside run' de uma vez. No entanto, com um yield (), uma vez que o encadeamento obtenha o controle, ele imprimirá a 'execução interna' uma vez e, em seguida, passará o controle para outro encadeamento, se houver. Se nenhum tópico estiver pendente, este tópico será retomado novamente. Portanto, toda vez que "Inside run" for executado, ele procurará outras threads para executar e, se nenhuma thread estiver disponível, a thread atual continuará em execução.
yield () é para dar espaço a outras threads importantes, join () é para esperar que outra thread conclua sua execução e interrupt () é para interromper uma thread atualmente em execução para fazer outra coisa.
fonte
Without a yield(), once the thread gets control it will execute the 'Inside run' loop in one go
? Por favor, esclareça.Thread.yield()
faz com que o thread passe do estado "Executando" para o estado "Executável". Nota: Não faz com que o thread vá para o estado "Esperando".fonte
RUNNING
estado parajava.lang.Thread
instâncias. Mas isso não impede um estado de "execução" nativo para o encadeamento nativo para o qual umaThread
instância é um proxy.Thread.yield ()
Junte-se()
fonte
O uso principal de yield () é colocar um aplicativo multi-threading em espera.
todas essas diferenças de métodos são yield () coloca o encadeamento em espera enquanto executa outro encadeamento e retorna após a conclusão desse encadeamento, join () trará o início dos encadeamentos em execução até o final e de outro encadeamento para executar após esse encadeamento terminado, interrupt () irá parar a execução de uma thread por um tempo.
fonte
yield
devem ser usados.