Neste capítulo, apresentaremos o método de espera/despertar do thread. O conteúdo envolvido inclui:
1. Introdução a wait (), notify (), notifyAll () e outros métodos
2. Espere () e notifique ()
3. Espere (tempo limite longo) e notifique ()
4. Wait () e notifyAll ()
5. Por que notificar (), esperar () e outras funções definidas em objeto, não thread
Introdução a wait (), notify (), notifyAll () e outros métodos
Em Object.java, interfaces como wait (), notify () e notifyAll () são definidas. A função de wait () é deixar o encadeamento atual entrar em um estado de espera e espera () também permitirá que o encadeamento atual libere a trava que ele mantém. O papel do notify () e NotifyAll () é acordar o thread de espera no objeto atual;
Os detalhes da API sobre espera/acordar na aula de objeto são os seguintes:
notify () - Acorde um único thread aguardando neste monitor de objeto.
notifyAll () - Acorde todos os threads esperando neste monitor de objeto.
espera () - Coloque o fio atual em um "estado de espera (bloqueio)" e "até que outros threads chamem o método notify () ou notifyAll () desse objeto", e o fio atual é despertado (inserido para o " estado pronto ").
Espere (tempo limite longo) - Que o tópico atual esteja em um "estado de espera (bloqueio)" e "até que outros threads chamem o método notify () ou notifyAll () desse objeto ou excedem a quantidade especificada de tempo", e o thread atual é despertado (digite "pronto").
Espere (tempo limite longo, int nanos) - Coloque o tópico atual em um "estado de espera (bloqueando)" até que outro thread chama o método notify () ou notifyAll () desse objeto, ou algum outro thread interrompe o encadeamento atual, ou a quantidade real de tempo excedeu ", e o thread atual é despertado (inserido para" Estado Pronto ").
2. Espere () e notificar () exemplos
A seguir, é apresentado um exemplo para demonstrar "wait () e notify () juntos".
A cópia do código é a seguinte:
// waittest.java código fonte
classe threada estende thread {
public threada (nome da string) {
super (nome);
}
public void run () {
sincronizado (this) {
System.out.println (Thread.currentThread (). GetName ()+"Call Notify ()");
// Acorde o tópico de espera atual
notificar ();
}
}
}
classe pública Waittest {
public static void main (string [] args) {
Threada T1 = new Threada ("T1");
sincronizado (t1) {
tentar {
// Iniciar "Thread T1"
System.out.println (thread.currentThread (). GetName ()+"start t1");
t1.start ();
// O thread principal aguarda o T1 acordar através do notify ().
System.out.println (thread.currentThread (). GetName ()+"wait ()");
t1.wait ();
System.out.println (thread.currentThread (). GetName ()+"continua");
} catch (interruptedException e) {
E.PrintStackTrace ();
}
}
}
}
Resultados em execução:
A cópia do código é a seguinte:
Iniciar principal t1
espera principal ()
T1 Call Notify ()
Continue principal
Resultados Descrição:
A figura a seguir ilustra o fluxo de "Thread Main" e "Thread T1".
(01) Observe que o "thread principal" na figura representa o "thread principal principal". "Thread T1" representa "Thread T1" começou no Waittest. E "Lock" representa "Bloqueio síncrono do objeto T1".
(02) "Thread Main" cria novo "Thread T1" através do novo ThreadA ("T1"). Em seguida, a "trava síncrona do objeto T1" é obtida por meio de sincronizado (T1). Em seguida, ligue para t1.start () para iniciar "Thread T1".
(03) "Principal Thread" executa T1.wait () para liberar "Lock of T1 Object" e entra no "estado de espera (bloqueio)". Aguarde os threads em objetos T1 acordem -o via notify () ou notifyAll ().
(04) Depois que "Thread T1" é executado, a trava "do objeto atual" é obtida por meio de sincronizado (este); o "tópico principal".
(05) Após a conclusão do "Thread T1", libere o "bloqueio do objeto atual". Imediatamente depois, o "thread principal" adquire o "bloqueio do objeto T1" e depois é executado.
Para o código acima? Um amigo perguntou uma vez: T1.wait () deve fazer "Thread T1" esperar; mas por que o "thread principal principal" espera?
Antes de responder a essa pergunta, vamos dar uma olhada em um parágrafo sobre espera no documento JDK:
A cópia do código é a seguinte:
Faz com que o encadeamento atual aguarde até que outro encadeamento invoca o método notify () ou o método notifyall () para este objeto.
Em outras palavras, esse método se comporta exatamente como se simplesmente executasse a chamada espera (0).
O thread atual deve possuir o monitor deste objeto. Pode ser provável a propriedade do monitor e retoma a execução.
O significado em chinês é aproximadamente:
Faz com que o "thread atual" espere até que outro encadeamento chama notify () ou notifyAll () acordar o thread. Em outras palavras, esse método tem o mesmo efeito que espera (0)! (Suplementar, para o método Wait (Long Millis), quando Millis é 0, significa infinito esperando até que seja acordado por notify () ou notifyAll ()).
Quando o "Thread atual" chama Wait (), ele deve ter o bloqueio de sincronização para o objeto. Depois que as chamadas de threads esperam (), o bloqueio será liberado; O thread continua a esperar até que reache o "Lock de sincronização para este objeto" e, em seguida, pode continuar a ser executado.
Nota: Na explicação do JDK, diz -se que a função de wait () é fazer com que o "Thread atual" aguarde e o "Thread atual" refere -se ao thread em execução na CPU!
Isso também significa que, embora o T1.Wait () seja o método Wait () chamado através de "Thread T1", o local onde o T1.Wait () é chamado está em "Principal principal do thread". O encadeamento principal deve ser o "encadeamento atual", ou seja, o estado em execução, antes que o T1.Wait () possa ser executado. Portanto, o "thread atual" neste momento é o "Principal Main Main"! Portanto, o t1.wait () é fazer com que o "thread principal" espere, não "thread t1"!
3. Espere (tempo limite longo) e notifique ()
Espere (tempo limite longo) colocará o fio atual em um "estado de espera (bloqueio)" e "até que outros threads chamem o método notify () ou notifyall () desse objeto ou excedem a quantidade especificada de tempo", e o O thread atual é despertado (inserido) "pronto").
O exemplo a seguir demonstra o tempo limite de espera (tempo limite) e o tópico é despertado.
A cópia do código é a seguinte:
// Código fonte de WaittimeoutTest.java
classe threada estende thread {
public threada (nome da string) {
super (nome);
}
public void run () {
System.out.println (thread.currentThread (). GetName () + "run");
// Um ciclo vicioso, funcionando continuamente.
Enquanto (verdadeiro)
}
}
classe pública waittimeouttest {
public static void main (string [] args) {
Threada T1 = new Threada ("T1");
sincronizado (t1) {
tentar {
// Iniciar "Thread T1"
System.out.println (thread.currentThread (). GetName () + "start t1");
t1.start ();
// O thread principal aguarda o T1 para acordar através do Notify () ou NotifyAll (), ou atrasos superiores a 3000ms;
System.out.println (thread.currentThread (). GetName () + "ligue para esperar");
T1.wait (3000);
System.out.println (thread.currentThread (). GetName () + "continua");
} catch (interruptedException e) {
E.PrintStackTrace ();
}
}
}
}
Resultados em execução:
A cópia do código é a seguinte:
Iniciar principal t1
Principal Call Wait
T1 Run // Após cerca de 3 segundos ... Saída "Principal Continue"
Continue principal
Resultados Descrição:
A figura a seguir ilustra o fluxo de "Thread Main" e "Thread T1".
(01) Observe que o "thread principal" na figura representa o thread principal waittimeouttest (ou seja, o thread principal). "Thread T1" representa o encadeamento T1 iniciado no Waittest. E "Lock" representa "Bloqueio síncrono do objeto T1".
(02) O thread principal principal executa T1.start () para iniciar o "Thread T1".
(03) A linha principal principal executa o T1.wait (3000) e, neste momento, o encadeamento principal entra no "estado de bloqueio". É necessário "o encadeamento usado para o bloqueio do objeto T1 para acordá -lo através do notify () ou notifyAll ()" ou "Após o tempo limite de 3000ms", o principal thread principal entra no "estado pronto" e depois pode ser executado.
(04) Depois que o "Thread T1" está em execução, ele entra em um loop morto e continua a ser executado.
(05) Após o tempo limite de 3000ms, o principal thread principal entrará no "Estado Ready" e depois entrará no "estado em execução".
4. Wait () e notifyAll ()
Através do exemplo anterior, sabemos que o notify () pode acordar um único thread esperando neste monitor de objeto.
Abaixo, demonstramos o uso do notifyAll () através de um exemplo;
A cópia do código é a seguinte:
classe pública NotifyAltest {
objeto estático privado obj = new Object ();
public static void main (string [] args) {
Threada T1 = new Threada ("T1");
Threada T2 = new Threada ("T2");
Threada T3 = new Threada ("T3");
t1.start ();
t2.start ();
t3.start ();
tentar {
System.out.println (Thread.currentThread (). GetName ()+"Sleep (3000)");
Thread.sleep (3000);
} catch (interruptedException e) {
E.PrintStackTrace ();
}
sincronizado (obj) {
// O tópico principal está esperando o despertar.
System.out.println (thread.currentThread (). GetName ()+"notifyAll ()");
obj.NotifyAll ();
}
}
classe estática threada estende thread {
public threada (nome da string) {
super (nome);
}
public void run () {
sincronizado (obj) {
tentar {
// Resultado da saída de impressão
System.out.println (thread.currentThread (). GetName () + "wait");
// Acorde o tópico de espera atual
obj.wait ();
// Resultado da saída de impressão
System.out.println (thread.currentThread (). GetName () + "continua");
} catch (interruptedException e) {
E.PrintStackTrace ();
}
}
}
}
}
Resultados em execução:
A cópia do código é a seguinte:
T1 Espere
Sono principal (3000)
T3 Espere
T2 Espere
principal notifyAll ()
T2 continue
T3 continue
T1 continue
Resultados Descrição:
Consulte o fluxograma abaixo.
(01) 3 threads "T1", "T2" e "T3" foram criados e iniciados no thread principal.
(02) O fio principal dorme por 3 segundos no sono (3000). Durante a linha principal, o sono por 3 segundos, assumimos que os três threads "T1", "T2" e "T3" estão todos em execução. Pegue "T1" como um exemplo. Também esperará que outros threads os acordem através de Nofity () ou NofityAll ().
(03) O fio principal dorme por 3 segundos e depois corre. Execute obj.NotifyAll () para acordar o tópico de espera no OBJ, ou seja, acorde os três threads "T1", "T2" e "T3". Imediatamente após a execução do segmento principal (OBJ) do fio principal, o encadeamento principal libera o "OBJ Lock". Dessa maneira, "T1", "T2" e "T3" podem obter o "Obj Lock" e continuar a correr!
5. Por que notificar (), esperar () e outras funções definidas em objeto, não thread
Funções como wait (), notify () no objeto, como sincronizado, operarão em "Lock de sincronização de objetos".
Espere () fará com que o "Thread atual" espere. não ser capaz de correr!
OK, depois que o thread chama Wait (), ele liberará o "bloqueio síncrono" mantido por sua fechadura; Agora, por favor, pense em uma pergunta: o que é notify () com base no despertar do tópico de espera? Ou qual é a correlação entre espera () e notify ()? A resposta é: com base no "bloqueio de sincronização de objetos".
O segmento responsável por acordar o segmento de espera (nós o chamamos de "Wake Up Thread"), ele só adquire a "trava de sincronização do objeto" (a trava da sincronização aqui deve ser a mesma que a trava de sincronização do segmento de espera), e chamadas notify () ou após notifyAll () Método, o thread de espera pode ser despertado. Embora o tópico de espera seja despertado; Você deve esperar até que o encadeamento de despertar libere o "bloqueio de sincronização do objeto" antes de obter o "bloqueio de sincronização do objeto" e continuar a ser executado.
Em resumo, notify (), espera () depende de "Lock Síncrono", que é mantido por bloqueios de objetos, e cada objeto tem e apenas um! É por isso que funções como notify (), wait () são definidas na classe de objeto, não na classe Thread.