Índice(?)[-]
Estende -se a classe JavalangThread dois implementa a interface JavalangRunnable três diferenças entre o encadeamento e a transição de estado de quatro roscas de quatro roscas Cinco encadeamento Agendando seis funções comuns explicando como usar por que o método de ingresso é usado sete thread comuns shread explicação
Este artigo fala principalmente sobre os métodos de uso de multi-threading em Java, sincronização de threads, transferência de dados do encadeamento, status do encadeamento e uso e visão geral da função de encadeamento.
Primeiro, vamos falar sobre a diferença entre um processo e um tópico:
Processo: Cada processo possui código e espaço de dados independentes (contexto do processo) e a alternância entre os processos terá uma grande sobrecarga. Um processo contém threads 1-N.
Tópico: O mesmo tipo de threads compartilham código e espaço de dados. Cada encadeamento possui uma pilha de corrida independente e contador de programas (PC), e a sobrecarga de comutação de encadeamento é pequena.
Como um processo, os threads são divididos em cinco estágios: criação, pronta, corrida, bloqueio e terminação.
Multipocess significa que o sistema operacional pode executar várias tarefas (programas) ao mesmo tempo.
Multithreading refere -se a vários fluxos seqüenciais sendo executados no mesmo programa.
Em Java, existem duas maneiras de implementar multi-threading. Um é continuar a classe Thread e o outro é implementar a interface executável.
1. Estenda a classe Java.Lang.Thread
pacote com.multithread.learning;/***@functon multithreading Learning*@autor lin bingwen*@time 2015.3.9*/classe thread1 estende thread {private string name; public thread1 (nome da string) {this.name = name; } public void run () {for (int i = 0; i <5; i ++) {System.out.println (nome + "run:" + i); tente {Sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printStackTrace (); }}}} classe pública Main {public static void main (string [] args) {thread1 mth1 = new Thread1 ("a"); Thread1 mth2 = new Thread1 ("b"); mth1.start (); mth2.start (); }} Saída:
Uma corrida: 0
B Run: 0
Uma corrida: 1
Uma corrida: 2
Uma corrida: 3
Uma corrida: 4
B Run: 1
B Run: 2
B Run: 3
B Run: 4
Execute novamente:
Uma corrida: 0
B Run: 0
B Run: 1
B Run: 2
B Run: 3
B Run: 4
Uma corrida: 1
Uma corrida: 2
Uma corrida: 3
Uma corrida: 4
ilustrar:
Quando o programa inicia e roda principal, a máquina virtual Java inicia um processo e o principal thread principal é criado quando o principal () é chamado. Com o método de início dos dois objetos de Mitisay, os outros dois threads também são iniciados, para que todo o aplicativo seja executado em vários threads.
NOTA: O método START () é chamado para não executar o código multithread imediatamente, mas faz com que o encadeamento se torne um estado executável. Quando é executado, é determinado pelo sistema operacional.
A partir dos resultados do programa em execução, podemos descobrir que os programas com vários threads são executados fora de ordem. Portanto, apenas o código executado fora de ordem deve ser projetado como multi-thread.
O objetivo do método Thread.Sleep () é impedir que o encadeamento atual ocupe os recursos da CPU obtidos apenas pelo processo, de modo a deixar um certo período de tempo para que outros threads sejam executados.
De fato, a ordem de execução de todo o código multithread é incerta e os resultados de cada execução são aleatórios.
No entanto, se o método inicial for chamado repetidamente, ocorrerá um java.lang.illegalthreadstateException.
Thread1 mth1 = new Thread1 ("A"); Thread1 mth2 = mth1; mth1.start (); mth2.start (); Saída:
Exceção no tópico "Main" java.lang.illegalthreadStateException
em java.lang.thread.start (fonte desconhecida)
em com.multithread.learning.main.main (main.java:31)
Uma corrida: 0
Uma corrida: 1
Uma corrida: 2
Uma corrida: 3
Uma corrida: 4
2. Implemente a interface java.lang.runnable
/***@functon multithreading Learning*@autor lin bingwen*@time 2015.3.9*/package com.multithread.runnable; classe thread2 implementa runnable {private string name; public Thread2 (nome da string) {this.name = name; } @Override public void run () {for (int i = 0; i <5; i ++) {System.out.println (nome + "run:" + i); tente {thread.sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printStackTrace (); }}}} classe pública principal {public static void main (string [] args) {new Thread (new Thread2 ("c")). start (); novo thread (novo thread2 ("d")). start (); }} Saída:
C Run: 0
D Run: 0
D Run: 1
C Run: 1
D Run: 2
C Run: 2
D Run: 3
C Run: 3
D Run: 4
C Run: 4
ilustrar:
A classe Thread2 implementa a interface executável, o que faz com que a classe tenha as características de uma classe multi-thread. O método run () é uma convenção para programas multithread. Todo o código multithread está no método de execução. A classe Thread é na verdade uma classe que implementa a interface executável.
Ao iniciar o múltiplo, você precisa primeiro construir o objeto através do encadeamento do construtor da classe Thread (Runnable Target) e, em seguida, ligue para o método START () do objeto Thread para executar o código multi-thread.
De fato, todo o código multithread é executado pelo método START () do Thread () do Thread. Portanto, se deve estender a classe Thread ou implementar a interface Runnable para implementar a multi-threading ou, finalmente, controlar os threads através da API do objeto Thread, familiarizando-se com a API da classe Thread é a base para a programação de múltiplas tenhas.
3. A diferença entre rosca e runnable
Se uma classe herdar thread, ele não é adequado para compartilhamento de recursos. No entanto, se a interface executável for implementada, é fácil implementar o compartilhamento de recursos.
pacote com.multithread.learning;/***@Functon Multi-Thread Learning, thread herdado, os recursos não podem ser compartilhados*@autor lin bingwen*@time 2015.3.9*/class Thread1 estende thread {private int conting = 5; nome de string privado; public thread1 (nome da string) {this.name = name; } public void run () {for (int i = 0; i <5; i ++) {System.out.println (nome + "run count =" + count--); tente {Sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printStackTrace (); }}}} classe pública Main {public static void main (string [] args) {thread1 mth1 = new Thread1 ("a"); Thread1 mth2 = new Thread1 ("b"); mth1.start (); mth2.start (); }} Saída:
B contagem de execução = 5
Uma contagem de execução = 5
B contagem de execução = 4
B contagem de execução = 3
B contagem de execução = 2
B contagem de execução = 1
Uma contagem de execução = 4
Uma contagem de execução = 3
Uma contagem de execução = 2
Uma contagem de execução = 1
Pelo acima, podemos ver que as contagens são diferentes entre diferentes threads, o que terá um grande problema para o sistema de venda de ingressos. Obviamente, a sincronização pode ser usada aqui. Vamos usar o Runnable para fazer isso aqui
/***@Functon Multi-Threading Learning Herits Runnable, os recursos podem ser compartilhados*@Autor Lin Bingwen*@Time 2015.3.9*/Package com.multithread.runnable; classe Thread2 implementa runnable {private int conting = 15; @Override public void run () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). GetName () + "run count =" + count--); tente {thread.sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printStackTrace (); }}}}} public class Main {public static void main (string [] args) {thread2 my = new Thread2 (); novo thread (my, "c"). start (); // o mesmo mt, mas não é possível no thread. Se você instanciar o objeto MT, uma exceção aparecerá novo thread (meu, "D"). Start (); novo thread (meu, "e"). start (); }} Saída:
C contagem de execução = 15
D Run Count = 14
E execução contagem = 13
D Run Count = 12
D Run Count = 10
D Run Count = 9
D Run Count = 8
C contagem de execução = 11
E contagem de execução = 12
C contagem de execução = 7
E contagem de execução = 6
C contagem de execução = 5
E contagem de execução = 4
C contagem de execução = 3
E contagem de execução = 2
Aqui, devemos observar que cada encadeamento usa o mesmo objeto de instanciação. Se não for o mesmo, o efeito será o mesmo que acima!
Resumir:
As vantagens de implementar a interface executável sobre a herdadora da classe Thread:
1): Adequado para vários tópicos com o mesmo código do programa para processar o mesmo recurso
2): Pode evitar a limitação da herança única em Java
3): Aumente a robustez do programa, o código pode ser compartilhado por vários threads, e o código e os dados são independentes
Deixe -me lembrá -lo: o método principal é na verdade um tópico. Em Java, os tópicos são iniciados ao mesmo tempo. Quanto a quando e qual é executado primeiro, depende inteiramente de quem recebe os recursos da CPU primeiro.
Em Java, pelo menos 2 threads são iniciados cada vez que o programa é executado. Um é o fio principal e o outro é o tópico de coleta de lixo. Porque sempre que uma classe é executada usando comandos Java, uma JVM será realmente iniciada e cada estágio da JVM inicia um processo no sistema operacional.
4. Transição do estado do thread
1. Novo estado (novo): um novo objeto de thread é criado.
2. Estado pronto (executável): Depois que o objeto Thread for criado, outros threads chamam o método START () do objeto. O tópico deste estado está localizado no pool de threads executáveis e se torna executável, esperando para obter os direitos de uso da CPU.
3. Estado em execução: o thread no estado pronto adquire a CPU e executa o código do programa.
4. Estado bloqueado: Estado bloqueado significa que o thread renuncia aos direitos de uso da CPU por algum motivo e para temporariamente a execução. Não é até o encadeamento entrar no estado pronto que ele tem a chance de ir ao estado de corrida. Existem três tipos de bloqueio:
(1) Esperando para bloquear: o encadeamento em execução executa o método wait (), e a JVM colocará o encadeamento no pool de espera.
(2) Bloqueio síncrono: quando o thread em execução adquire o bloqueio de sincronização do objeto, se o bloqueio de sincronização for ocupado por outros threads, a JVM colocará o thread no pool de bloqueio.
(Iii), outro bloqueio: quando um thread em execução executa o método Sleep () ou junção () ou emite uma solicitação de E/S, a JVM definirá o thread como um estado de bloqueio. Quando o Sleep () State se estendeu, a junção () esperou que o thread termine ou o tempo de tempo ou o processamento de E/S tenha sido concluído, o thread reinseriu para o estado pronto.
5. Estado morto: o thread terminou de executar ou sair do método run () devido a uma exceção e o thread termina seu ciclo de vida.
5. AGENÇÃO DO TRANSEIRO
Agenda do tópico
1. Ajuste a prioridade do encadeamento: os threads java têm prioridade e os threads com alta prioridade terão mais oportunidades de execução.
A prioridade dos threads java é representada por números inteiros, com um intervalo de valor de 1 ~ 10. A aula de threads tem as três constantes estáticas a seguir:
estático int max_priority
A maior prioridade que um encadeamento pode ter é 10.
estático int min_priority
A menor prioridade que um encadeamento pode ter é 1.
estático int norma_prioridade
A prioridade padrão atribuída ao thread é 5.
Os métodos setPriority () e getPriority () da classe Thread são usados para definir e obter a prioridade do thread, respectivamente.
Cada encadeamento tem uma prioridade padrão. A prioridade padrão do encadeamento principal é o thread.Norm_Priority.
A prioridade dos threads é herdada. Por exemplo, se o thread b for criado no thread a, então B terá a mesma prioridade que A.
A JVM fornece 10 prioridades de threads, mas não mapeia bem com sistemas operacionais comuns. Se você deseja que o programa seja transportado para cada sistema operacional, use apenas a classe Thread com as três constantes estáticas a seguir como prioridade, o que pode garantir que a mesma prioridade adote o mesmo método de agendamento.
2. Sleep Thread: Thread.Sleep (Millis longo) Método para fazer com que o thread vá para um estado de bloqueio. O parâmetro Millis define o tempo de sono em milissegundos. Quando o sono termina, ele se torna executável. A plataforma Sleep () tem boa portabilidade.
3. Aguarda do thread: o método wait () na classe de objeto faz com que o encadeamento atual aguarde até que outros threads chamem o método notify () notify () ou notifyall () do objeto. Esses dois métodos de despertar também são métodos na classe de objeto, e seu comportamento é equivalente a chamar Wait (0).
4. Concessões do thread: Thread.yield () O método suspende o objeto Thread atualmente executando e oferece a oportunidade de execução para tópicos com a mesma ou maior prioridade.
5. Método de junção do thread: junção (), esperando que outros threads terminem. Chamando o método junção () de outro thread no encadeamento atual, o thread atual vai para um estado de bloqueio até que o outro processo funcione e o thread atual passa do bloqueio ao estado pronto.
6. AVISO DE THREADOR: O método notify () na classe de objeto acorda um único thread aguardando neste monitor de objeto. Se todos os threads estiverem aguardando esse objeto, um dos threads será selecionado. A escolha é arbitrária e ocorre ao tomar uma decisão sobre a implementação. O thread espera no monitor do objeto chamando um dos métodos de espera. O encadeamento acordado não pode ser executado até que o encadeamento atual abandone a trava nesse objeto. O fio despertador competirá com todos os outros threads sincronizados ativamente no objeto de maneira convencional; Por exemplo, o encadeamento despertado não possui privilégios ou desvantagens confiáveis em ser o próximo thread que bloqueia esse objeto. Um método semelhante também possui um notifyAll () que acorda todos os threads esperando neste monitor de objeto.
NOTA: Os dois métodos suspender () e currume () no thread foram abolidos no JDK1.5 e não serão introduzidos novamente. Porque há uma tendência ao impasse.
6. Descrição das funções comuns
① Sono (Millis Long): Deixe o fio de tópico atualmente executado dentro do número especificado de milissegundos (suspender a execução)
②Join (): refere -se a aguardar o thread T.
Como usá -lo.
Juntar é um método da classe de threads. É chamado diretamente após o início do thread. Ou seja, a função de junção () é: "Aguarde o thread terminando". O que precisa ser entendido aqui é que o thread se refere ao encadeamento principal que aguarda o thread infantil encerrar. Ou seja, o código depois que o encadeamento filho chama o método junção () e só pode ser executado até que o thread infantil seja concluído.
Thread t = new athread (); t.start (); T.Join ();
Por que usar o método junção ()
Em muitos casos, o encadeamento principal gera e inicia o tópico infantil. Se um grande número de operações demoradas forem necessárias no encadeamento filho, o encadeamento principal geralmente terminará antes do thread infantil. No entanto, se o thread principal precisar usar o resultado do processamento do thread da criança após o processamento de outras transações, ou seja, o thread principal precisará aguardar o tópico infantil concluir a execução antes de terminar. Neste momento, o método junção () deve ser usado.
Sem ingressar. /** *@Functon Multithreading Learning, ingressar *@autor lin bingwen *@time 2015.3.9 */package com.multithread.join; classe thread1 estende o thread {private string name; public thread1 (nome da string) {super (nome); this.name = nome; } public void run () {System.out.println (thread.currentThread (). getName () + "thread inicia!"); for (int i = 0; i <5; i ++) {System.out.println ("subthRead"+nome+"run:"+i); tente {Sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printStackTrace (); }} System.out.println (thread.currentThread (). GetName () + "thread run termina!"); }} classe pública Main {public static void main (string [] args) {System.out.println (thread.currentThread (). getName ()+"thread principal comece!"); Thread1 mth1 = new Thread1 ("A"); Thread1 mth2 = new Thread1 ("b"); mth1.start (); mth2.start (); System.out.println (Thread.currentThread (). GetName ()+ "Principal Run Run Ends!"); }} Resultado da saída:
O encadeamento principal começa a correr!
O tópico principal principal termina!
B A execução do tópico começa!
Raições de tópico infantil: 0
Uma corrida de thread começa!
Tópico infantil A executa: 0
Raições de tópico infantil: 1
Tópico infantil A corridas: 1
Tópico de criança A corridas: 2
Tópico infantil A corridas: 3
Tópico infantil A corridas: 4
Uma corrida de thread termina!
Raições de tópico infantil: 2
Raições de tópico infantil: 3
Raições de tópico infantil: 4
B O tópico corre!
Descobriram que o tópico principal terminou mais cedo do que o tópico infantil
Juntar
public class Main {public static void main (string [] args) {System.out.println (thread.currentThread (). getName ()+"thread principal run start!"); Thread1 mth1 = new Thread1 ("A"); Thread1 mth2 = new Thread1 ("b"); mth1.start (); mth2.start (); tente {mth1.join (); } catch (interruptedException e) {e.printStackTrace (); } tente {mth2.join (); } catch (interruptedException e) {e.printStackTrace (); } System.out.println (thread.currentThread (). GetName ()+ "o thread principal termina!"); }} Resultados em execução:
O encadeamento principal começa a correr!
Uma corrida de thread começa!
Tópico infantil A executa: 0
B A execução do tópico começa!
Raições de tópico infantil: 0
Tópico infantil A corridas: 1
Raições de tópico infantil: 1
Tópico de criança A corridas: 2
Raições de tópico infantil: 2
Tópico infantil A corridas: 3
Raições de tópico infantil: 3
Tópico infantil A corridas: 4
Raições de tópico infantil: 4
Uma corrida de thread termina!
O fio principal definitivamente esperará até que os threads da criança sejam concluídos antes que termine.
③yield (): faz uma pausa no objeto Thread atualmente executando e executa outros threads.
A função do método Thread.yield () é: pause o objeto Thread atualmente executado e execute outros threads.
O que o rendimento () deve fazer é obter o tópico em execução atual de volta ao estado executável para permitir que outros threads com a mesma prioridade tenham uma oportunidade de execução. Portanto, o objetivo de usar o rendimento () é permitir que os threads da mesma prioridade tenham desempenho adequado. No entanto, na realidade, o rendimento () não pode ser garantido para alcançar o objetivo da concessão, porque o encadeamento de concessão pode ser selecionado novamente pelo agendador de threads.
Conclusão: O rendimento () nunca faz com que o tópico vá para o estado de espera/sono/bloqueio. Na maioria dos casos, o rendimento () fará com que o thread passasse do estado de corrida para a corrida, mas pode não funcionar. Você pode ver a imagem acima.
/** *@Functon multithreading Learning rendimento *@autor lin bingwen *@time 2015.3.9 */package com.multithread.yield; classe threadyield estende thread {public threadyield (nome da string) {super (nome); } @Override public void run () {for (int i = 1; i <= 50; i ++) {System.out.println ("" + this.getName () + "-----" + i); // Quando eu tiver 30 anos, o thread desistirá do tempo da CPU e permitirá que outros threads ou seus próprios threads sejam executados (ou seja, quem o agarra primeiro o executa) se (i == 30) {this.yield (); }}}} classe pública principal {public static void main (string [] args) {threadyield yt1 = new Threadyield ("zhang san"); Threadyield yt2 = new Threadyield ("li si"); yt1.start (); yt2.start (); }} Resultados em execução:
O primeiro caso: Li Si (Thread) obterá o tempo de CPU quando for executado em 30. Nesse momento, o Zhang San (Thread) pega o tempo da CPU e o executa.
A segunda situação: quando Li Si (Thread) é executado para 30, o tempo da CPU será abandonado. Neste momento, o Li Si (Thread) pega o tempo da CPU e o executa.
A diferença entre o sono () e o rendimento ()
A diferença entre o sono () e o rendimento ()): Sleep () faz com que o thread atual insira um estado estagnado; portanto, o encadeamento que executa o sono () definitivamente não será executado dentro do tempo especificado; O rendimento () apenas faz com que o encadeamento atual retorne ao estado executável, para que o thread executando rendimento () possa ser executado imediatamente após a entrada do estado executável.
O método do sono faz com que o tópico atualmente em execução durma por um período de tempo e entra em um estado invasável. A duração deste período é definida pelo programa. O método de rendimento permite que o encadeamento atual desista da propriedade da CPU, mas o tempo da transferência é inadequado. De fato, o método de rendimento () corresponde à seguinte operação: primeiro verifique se existem threads com a mesma prioridade atualmente no mesmo estado executável. Nesse caso, entregue a propriedade da CPU a este thread, caso contrário, continue executando o thread original. Portanto, o método de rendimento () é chamado de "concessão", o que oferece a oportunidade de outros tópicos com a mesma prioridade
Além disso, o método do sono permite que os threads de prioridade mais baixa tenham oportunidades de execução, mas quando o método de rendimento () é executado, o encadeamento atual ainda está em um estado executável, por isso é impossível desistir de threads de prioridade mais baixos para obter a propriedade da CPU mais tarde. Em um sistema em execução, se o encadeamento de prioridade mais alta não chamar o método do sono e não for bloqueado pela E/S, o encadeamento de prioridade mais baixa só poderá aguardar que todos os threads de prioridade mais alta sejam executados para ter uma chance de executar.
④setProriority (): altere a prioridade do thread.
Min_priority = 1
Norm_priority = 5
Max_priority = 10
uso:
Thread4 T1 = novo Thread4 ("T1");
Thread4 T2 = novo Thread4 ("T2");
t1.setPriority (Thread.max_priority);
T2.SetPriority (Thread.min_Priority);
⑤Interrupt (): interrompa um thread. Esse método final é bastante difícil. Se o thread t abre um recurso e não tiver tido tempo de fechá -lo, ou seja, o método de execução será forçado a encerrar o thread antes de ser executado, o que fará com que o recurso não feche o fechamento.
A melhor maneira de encerrar o processo é usar o programa de exemplo da função Sleep (). Uma variável booleana é usada na classe Thread para controlar quando o método run () termina. Quando o método run () termina, o thread termina.
⑥wait ()
Obj.wait () e obj.Notify () devem ser usados com sincronizado (OBJ), ou seja, aguarde e notificar operando no bloqueio OBJ que foi adquirido. Do ponto de vista sincronizado, é obj.wait (), e obj.notify deve estar no bloco de instrução sincronizado (obj) {...}. De uma perspectiva funcional, espera significa que, depois que o thread adquire o bloqueio do objeto, ele libera ativamente o bloqueio do objeto e o encadeamento dorme. O bloqueio do objeto não pode ser obtido e a execução continuará até que outro thread chamar o objeto notify () para acordar o thread. O Notify () correspondente é a operação de despertar do bloqueio do objeto. Mas uma coisa a observar é que, após a chamada notificada (), o bloqueio do objeto não é liberado imediatamente, mas a execução do bloco de instrução Synchronized () {} correspondente é concluído e o bloqueio é liberado automaticamente, a JVM selecionará aleatoriamente um encadeamento do segmento do objeto de espera (), atribua -o à trava do objeto, despertará o fio e continuará tomando a tom. Isso fornece operações de sincronização e despertar entre threads. Thread.sleep () e object.wait () podem pausar o encadeamento atual e liberar o controle da CPU. A principal diferença é que, embora o objeto.wait () libere a CPU, ele libera o controle do bloqueio do objeto.
Não basta entender conceitualmente e precisa ser testado em exemplos práticos para entender melhor. O exemplo mais clássico da aplicação do object.wait () e object.Notify () deve ser o problema de imprimir ABC com três threads. Esta é uma pergunta de entrevista relativamente clássica, e as perguntas são as seguintes:
Estabeleça três threads, Thread A Imprime 10 vezes, thread b impressa B 10 vezes, thread C Impressa C 10 vezes, o encadeamento C requer que o thread seja executado ao mesmo tempo e o ABC é impresso alternadamente 10 vezes. Esse problema pode ser facilmente resolvido usando o objeto Wait () e notify (). O código é o seguinte:
/** * Espere o uso * @Author Dreamsea * @Time 2015.3.9 */package com.multithread.wait; public classe mythreadprinter2 implementa runnable {private string name; objeto privado prev; objeto privado; mythreadPrinter2 privado (nome da string, objeto prev, objeto self) {this.name = name; this.prev = prev; this.Self = self; } @Override public void run () {int count = 10; while (count> 0) {sincronizado (prev) {sincronizado (self) {System.out.print (nome); contar--; self.Notify (); } tente {prev.wait (); } catch (interruptedException e) {e.printStackTrace (); }}}} public static void main (string [] args) lança Exceção {objeto a = new Object (); Objeto b = new Object (); Objeto c = novo objeto (); MythreadPrinter2 PA = new MythreadPrinter2 ("A", C, A); MythreadPrinter2 pb = new MythreadPrinter2 ("b", a, b); MythreadPrinter2 pc = new MythreadPrinter2 ("C", B, C); novo thread (PA) .start (); Thread.sleep (100); // Certifique -se de executar um novo thread (pb) .start (); Thread.sleep (100); }} Resultado da saída:
Abcabcabcabcabcabcabcabcabcabcabcabcabcabc
Vamos primeiro explicar sua ideia geral. De uma perspectiva geral, esse problema é uma operação de despertar síncrona entre três threads. O objetivo principal é executar três threads em Threada-> Threadb-> Threadc-> Threada Loop. Para controlar a ordem da execução do encadeamento, a ordem de despertar e espera deve ser determinada; portanto, cada encadeamento deve manter dois bloqueios de objetos ao mesmo tempo antes de continuar a execução. Um bloqueio de objeto é Anterior, que é a trava do objeto mantida pelo thread anterior. Outro é a fechadura do objeto. A idéia principal é que, para controlar a ordem de execução, você deva primeiro manter o previsto o previsto, ou seja, o encadeamento anterior deve liberar seu próprio bloqueio de objeto e depois aplicar seu próprio bloqueio de objeto. Imprima quando ambos são ambos. Em seguida, primeiro chame a si mesmo.Notify () para liberar seu próprio travamento de objeto, acorde o próximo thread de espera e depois ligue para prev.wait () para liberar o bloqueio do objeto Prev, encerrar o thread atual e aguarde o loop ser despertado novamente. Execute o código acima e você poderá descobrir que três threads imprimem o ABC em um loop, um total de 10 vezes. O principal processo de execução do programa é que o thread a é o primeiro a ser executado, mantém os trava do objeto de C e A e, em seguida, libera os bloqueios de A e C e acorda B. Thread Bande pelo bloqueio A e, em seguida, se aplica a bloqueio B, depois a bloqueio B, e depois a trava, e depois a trava, e depois a trava, e depois a bloqueio B, e depois a trava, e depois a bloqueio B. Você pensa com cuidado, descobrirá que há um problema, que é a condição inicial. Os três threads são iniciados na ordem de A, B e C. De acordo com os pensamentos anteriores, A acorda B, B acorda C, C e depois acorda A. No entanto, essa suposição depende da ordem de agendamento e execução do thread na JVM.
A diferença entre espera e sono
Pontos comuns:
1. Eles estão todos em um ambiente multithread e podem bloquear o número especificado de milissegundos na chamada e retornar do programa.
2. Aguarde () e Sleep () podem interromper o estado de pausa do thread através do método interrupto (), para que o thread ligue imediatamente uma interrupção.
Se o thread A quiser encerrar o thread B imediatamente, o método de interrupção poderá ser chamado na instância do thread correspondente ao encadeamento B. Se o thread B estiver esperando/sono/junte -se neste momento, o Thread B lançará imediatamente uma interrupção e o retornará diretamente em Catch () {} para encerrar com segurança o thread.
Deve -se notar que a interrupção interrompida é jogada pelo próprio thread por dentro, não pelo método interrupto (). Quando o interrupção () é chamado em um thread, se o thread estiver executando o código normal, o thread não lançará uma interrupção. No entanto, uma vez que o thread entrar no wait ()/sleep ()/junção (), uma interrupção será lançada imediatamente.
Diferenças:
1. Métodos da classe de thread: Sleep (), rendimento (), etc.
Métodos de objeto: wait () e notify (), etc.
2. Cada objeto possui um bloqueio para controlar o acesso síncrono. A palavra -chave sincronizada pode interagir com o bloqueio do objeto para realizar a sincronização do encadeamento.
O método do sono não libera o bloqueio, enquanto o método de espera libera o bloqueio, para que outros threads possam usar blocos ou métodos de controle síncrono.
3. Aguarde, notifique e notifiquell só pode ser usado em métodos de controle de sincronização ou blocos de controle de sincronização, enquanto o sono pode ser usado em qualquer lugar.
4. O sono deve capturar exceções, enquanto espera, notificar e notificar todos os que não precisam capturar exceções; portanto, a maior diferença entre os métodos Sleep () e Wait () é:
Quando dormir () dorme, mantenha a trava do objeto e ainda possui a fechadura;
Quando espera () dorme, o bloqueio do objeto é liberado.
No entanto, Wait () e Sleep () podem interromper o estado de pausa do thread através do método interromp (), para que o thread jogue imediatamente uma interruptedException (mas não é recomendável usar esse método).
Sleep () Método
O sono () faz com que o encadeamento atual entre em um estado estagnado (bloqueia o fio atual), desistir do uso do copo, e o objetivo é impedir que o fio atual ocupe os recursos da CPU obtidos apenas pelo processo, de modo a deixar um certo tempo para que outros threads sejam executados;
Sleep () é um método estático da classe Thread; Portanto, ele não pode alterar o bloqueio da máquina do objeto; portanto, ao chamar o método Sleep () em um bloco sincronizado, embora o encadeamento esteja inativo, a trava da máquina do objeto não é liberada e outros threads não podem acessar o objeto (embora ainda esteja mantendo a trava do objeto mesmo quando está dormindo).
Após o sono () do sono () o tempo do sono expira, o thread não é necessariamente executado imediatamente, porque outros threads podem estar em execução e não estão programados para abandonar a execução, a menos que o thread tenha uma prioridade mais alta.
Método Wait ()
O método wait () é um método na classe de objeto; Quando um thread executa o método wait (), ele entra em um pool de espera relacionado ao objeto e, ao mesmo tempo, perde (libera) a trava da máquina do objeto (perde temporariamente o bloqueio da máquina e o tempo limite de espera (tempo limite) também precisará devolver o bloqueio do objeto); Outros tópicos podem acessá -lo;
espera () usa notificar ou notificar todos ou tempo de sono especificado para acordar o thread no pool de espera atual.
Wiat () deve ser colocado no bloco sincronizado, caso contrário, a exceção "java.lang.illegalmonitorStateException" será lançada quando o tempo de execução do programa.
7. Termos comuns dos threads Explicação
Tópico principal: o thread gerado pelo programa de chamadas JVM Main ().
Tópico atual: este é um conceito confuso. Geralmente refere -se a um processo obtido através do thread.CurrentThread ().
Tópico de fundo: refere -se a um thread que fornece serviços a outros threads, também conhecidos como um thread daemon. O thread de coleta de lixo da JVM é um thread de fundo. A diferença entre um thread do usuário e um thread daemon é se espera que o encadeamento principal termine o encadeamento de primeiro plano, dependendo da extremidade do encadeamento principal: refere -se ao encadeamento que aceita o serviço do encadeamento de segundo plano. De fato, os fios de fundo em primeiro plano são conectados juntos, assim como o relacionamento entre o fantoche e o manipulador dos bastidores. O boneco é o fio de primeiro plano, e o manipulador dos bastidores é o fio de fundo. Os threads criados por threads em primeiro plano também são threads em primeiro plano por padrão. Você pode usar os métodos ISDAEMON () e SetDaemon () para determinar e definir se um thread é um encadeamento em segundo plano.
Alguns métodos comuns de classes de threads:
Sono (): Força um fio para dormir em n milissegundos.
Isalive (): determina se um fio sobrevive.
ingressar (): aguarde o thread para terminar.
ActiveCount (): o número de threads ativos no programa.
enumerar (): enumerar fios no programa.
currentThread (): obtém o thread atual.
ISDAEMON (): se um thread é um thread daemon.
setDaemon (): defina um thread como um thread daemon. (A diferença entre um tópico do usuário e um tópico daemon é se espera que o thread principal termine, dependendo do final do thread principal)
setName (): defina um nome para o thread.
espere (): force um tópico para esperar.
notify (): notifique um thread para continuar em execução.
SetPriority (): define a prioridade de um thread.
8. Sincronização de threads
1. Existem dois escopos de palavras -chave sincronizadas:
1) Está dentro de uma instância de objeto. Amethod () {} sincronizado pode impedir que vários threads acessem o método sincronizado desse objeto ao mesmo tempo (se um objeto tiver vários métodos sincronizados, desde que um encadeamento acesse um dos métodos sincronizados, outros threads não podem acessar nenhum método sincronizado no objeto ao mesmo tempo). No momento, o método sincronizado de diferentes instâncias de objetos é ininterrupto. That is to say, other threads can still access the synchronized method in another object instance of the same class at the same time;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;
3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键字就可以轻松地解决多线程共享数据同步问题。到底如何?还得对synchronized关键字的作用进行深入了解才可定论。
总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
在进一步阐述之前,我们需要明确几点:
A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁而且同步方法很可能还会被其他线程的对象访问。
B.每个对象只有一个锁(lock)与之相关联。
C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
接着来讨论synchronized用到不同地方对代码产生的影响:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。
1. 把synchronized当作函数修饰符时,示例代码如下:
Public synchronized void methodAAA(){//….}这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。
上边的示例代码等同于如下代码:
public void methodAAA(){synchronized (this) // (1){ //…..}}(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(
2.同步块,示例代码如下:
public void method3(SomeObject so) { synchronized(so){ //…..}}这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable{ private byte[] lock = new byte[0]; // 特殊的instance变量Public void methodA(){ synchronized(lock) { //… }}//…..}注:零长度的byte数组对象创建起来将比任何对象都经济查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo{public synchronized static void methodAAA() // 同步的static 函数{//….}public void methodBBB(){ synchronized(Foo.class) // class literal(类名称字面常量)} }代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将Foo.class和P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
九、线程数据传递
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。
9.1、通过构造方法传递数据在创建线程时,必须要建立一个Thread类的或其子类的实例。因此,我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来,以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据:
package mythread; public class MyThread1 extends Thread { private String name; public MyThread1(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { Thread thread = new MyThread1("world"); thread.start(); }}由于这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。因此,要想避免这种情况,就得通过类方法或类变量来传递数据。
9.2、通过变量和方法传递数据
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。下面的代码是对MyThread1类的改版,使用了一个setName方法来设置name变量:
package mythread; public class MyThread2 implements Runnable { private String name; public void setName(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { MyThread2 myThread = new MyThread2(); myThread.setName("world"); Thread thread = new Thread(myThread); thread.start(); }} 9.3、通过回调函数传递数据
上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,如在下面代码的run方法中产生了3个随机数,然后通过Work类的process方法求这三个随机数的和,并通过Data类的value将结果返回。从这个例子可以看出,在返回value之前,必须要得到三个随机数。也就是说,这个value是无法事先就传入线程类的。
package mythread; class Data { public int value = 0; } class Work { public void process(Data data, Integer numbers) { for (int n : numbers) { data.value += n; } } } public class MyThread3 extends Thread { private Work work; public MyThread3(Work work) { this.work = work; } public void run() { java.util.Random random = new java.util.Random(); Data data = new Data(); int n1 = random.nextInt(1000); int n2 = random.nextInt(2000); int n3 = random.nextInt(3000); work.process(data, n1, n2, n3); // Use the callback function System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+" + String.valueOf(n3) + "=" + data.value); } public static void main(String[] args) { Thread thread = new MyThread3(new Work()); thread.start(); }}The above is a detailed explanation of Java multi-threading. I hope it can help you learn this part of the knowledge. Obrigado pelo seu apoio a este site!