Este artigo estuda principalmente o código de amostra do aplicativo de condição de simultaneidade Java Condição de bloqueio, como segue.
A condição quebra os métodos de monitor de objetos (aguarde, notificar e notificar todos) em objetos completamente diferentes, para que, ao combinar esses objetos com qualquer implementação de bloqueio, cada objeto forneça vários conjuntos de espera (conjunto de espera). Entre eles, o bloqueio substitui o uso de métodos e declarações sincronizados, e a condição substitui o uso de métodos de monitor de objeto.
Como a condição pode ser usada para substituir a espera, notificar e outros métodos, podemos comparar o código de comunicação entre thread escrita antes e dar uma olhada no problema original:
Existem dois tópicos. O encadeamento infantil é executado 10 vezes primeiro, depois o encadeamento principal é executado 5 vezes e depois muda para o encadeamento filho executa 10 e, em seguida, o encadeamento principal é executado 5 vezes ... Esta viagem de ida e volta é 50 vezes.
Eu usei Wait and Notify para implementá -lo antes, mas agora uso a condição para reescrevi, o código é o seguinte:
classe pública ConditionCommunication {public static void main (string [] args) {Business Business = new Business (); new Thread (new Runnable () {// Abra um thread filho @Override public void run () {para (int i = 1; i <= 50; i ++) {BUSSINEY.SUB (i);}}; 50; {condition.await (); // Use a condição para chamar o método aguardando} catch (Exceção e) {// TODO GENERATO AUTOMENTADO Blocke.printStackTrace ();}} para (int j = 1; j <= 10; false; condition.signal (); // Use a condição para enviar um sinal de despertar e acordar uma certa} finalmente {Lock.unlock ();}} public void main (int i) {Lock.lock (); Tente {while Await MethodsUsb) {try {condition.await (); // Use a condição para o Await Methods} blocke.printStackTrace ();}} para (int j = 1; j <= 10; j ++) {System.out.println ("sequência de rosca principal de" + j + ", loop de" + i);} bshouldsub = true;Do ponto de vista do código, a condição é usada em conjunto com o bloqueio. Sem bloqueio, a condição não pode ser usada, porque a condição é criada através do bloqueio. Este uso é muito simples. Enquanto você dominar o uso de sincronizado, aguarde e notificar, você pode dominar completamente o uso de bloqueio e condição.
O acima usa bloqueio e condição em vez de métodos sincronizados e monitorados de objeto para realizar a comunicação entre dois threads. Agora, vamos escrever um aplicativo um pouco mais avançado: simular a fila de bloqueio do buffer.
O que é um buffer? Por exemplo, há muitas pessoas que desejam enviar mensagens agora. Sou uma estação de trânsito e quero ajudar outras pessoas a enviar mensagens. Então agora eu preciso fazer duas coisas. Uma coisa é receber mensagens enviadas pelo usuário e colocá -las no buffer em ordem. A outra coisa é exibir mensagens enviadas pelo usuário em ordem do buffer e enviá -las.
Agora abstrair esse problema real: um buffer é uma matriz. Podemos escrever dados na matriz ou tirar dados da matriz. As duas coisas que preciso fazer são iniciar dois threads, um para armazenar dados e outro para obter dados. Mas o problema é que, se o buffer estiver cheio, significa que há muitas mensagens recebidas, ou seja, a mensagem enviada é muito rápida e outro thread da minha vida não é suficiente para enviá -lo, resultando no buffer que está sendo deixado de fora; portanto, o fio do armazenamento de dados deve ser bloqueado e deixá -lo esperar; Pelo contrário, se eu o encaminhar muito rápido, e agora todo o conteúdo do buffer foi enviado por mim e nenhuma nova mensagem foi enviada pelo usuário, o encadeamento da aquisição de dados deve ser bloqueado no momento.
OK, depois de analisar a fila de bloqueio deste buffer, vamos usar a tecnologia de condição para implementá -la:
Classe buffer {Final Lock Lock = new Reentrantlock (); // Defina uma condição final de trava notfull = Lock.Newcondition (); // Definir condição da condição NotEmpty = Lock.Newcondition (); // Definir ConditionFinal Object [] itens = novo objeto [10]; count; // Os subscritos da matriz são usados para calibrar a posição // Dados de armazenamento na fila public void put (objeto x) lança interruptedException {Lock.lock (); // bloqueio try {while (count == items.length) {System.out.println (thread.currentThread (). sendo! "); notfull.await (); // Se a fila estiver cheia, bloqueie o fio do armazenamento de dados, esperando para ser acordado} // se não estiver cheio, armazene itens [putptr] = x; if (++ putptr == items.length) // Este é o julgamento que atinge o final da matriz. Se for alcançado, volte ao início putptr = 0; ++ contagem; // Número de mensagens System.out.println (thread.currentThread (). GetName () + "Salve o valor:" + x); notempty.signal (); // ok, agora há dados na fila. Acorde o tópico com fila vazia e você pode obter os dados} finalmente {Lock.unlock (); // download the Lock}} // busca dados do objeto público da fila Take () lança interruptedException {Lock.lock (); Por enquanto! "); NotEmpty.await (); // Se a fila estiver vazia, o fio bloqueará os dados para recuperar o fio, esperando para ser acordado} // se não estiver vazio, pegue o objeto x = itens [TakePtr]; se (++ Takeptr == itens.Length) // Jude se ele atinge a extremidade; System.out.println (thread.currentThread (). GetName () + "retire o valor:" + x); notfull.signal (); // ok, agora há um local na fila. Acorde o tópico cheio da fila e você poderá armazenar dados. Retornar x;} finalmente {Lock.unlock (); // Lock}}}Este programa é clássico, tirei -o da documentação oficial do JDK e adicionei comentários. Existem duas condições definidas no programa, que são usadas para executar os dois threads, e esperar e acordar, respectivamente. A ideia é clara e o programa também é muito robusto. Uma pergunta pode ser considerada: por que você precisa usar dois códigos? Deve haver uma razão para esse design. Se você usar uma condição, agora suponha que a fila esteja cheia, mas existem 2 threads A e B que armazenam dados ao mesmo tempo, todos entram no sono. Ok, agora outro tópico tira um e depois acorda um dos threads a, então a pode salvá -lo. Depois de salvar, um acorda outro tópico. Se B estiver despertado, haverá um problema, porque a fila está cheia neste momento e B não pode armazená -la. Se B Stores, ele substituirá o valor que não foi recuperado. Como uma condição é usada, tanto o armazenamento quanto a retirada usam essa condição para dormir e acordar, e ela será confusa. Neste ponto, você pode entender o uso dessa condição. Agora vamos testar o efeito da fila de bloqueio acima:
public class BoundedBuffer {public static void main (String [] args) {buffer buffer = new buffer (); para (int i = 0; i <5; i ++) {// Abra 5 threads para armazenar dados no buffer new; (InterruptEdException e) {e.printStackTrace ();}}}). Start ();} para (int i = 0; i <10; i ++) {// Abra 10 threads para obter dados do buffer newSh Thread (new Runnable () {@override public void run () {Try {Cuffer.Te {E.PrintStackTrace ();}}}). start ();}}}}Eu deliberadamente permiti que apenas 5 threads armazenassem dados e 10 threads para buscar dados, apenas para que sejam impedidos de obter dados e ver os resultados da operação:
O thread-5 está bloqueado e os dados não podem ser recuperados por enquanto!
O Thread-10 está bloqueado e os dados não podem ser recuperados por enquanto!
Tópico 1 Valor salvo: 755
Valor salvo Thread-0: 206
Tópico 2 Valor salvo: 741
Tópico 3 Valor salvo: 381
Thread-14 Valor removido: 755
Tópico-4 Valor salvo: 783
Thread-6 Retire o valor: 206
Thread-7 Valor removido: 741
Thread-8 Valor removido: 381
Thread-9 retire o valor: 783
O thread-5 está bloqueado e os dados não podem ser recuperados por enquanto!
O thread-11 está bloqueado e os dados não podem ser recuperados por enquanto!
Thread-12 está bloqueado e os dados não podem ser recuperados por enquanto!
O Thread-10 está bloqueado e os dados não podem ser recuperados por enquanto!
Thread-13 está bloqueado e os dados não podem ser recuperados por enquanto!
A partir dos resultados, podemos ver que os threads 5 e 10 são executados primeiro e eles descobrem que não há na fila, para que sejam bloqueados e durmam lá. Eles só podem obtê -lo até que um novo valor seja armazenado na fila. No entanto, eles não têm sorte e os dados armazenados são obtidos primeiro por outros threads. Haha ... eles podem executá -lo mais algumas vezes. Se você deseja ver que os dados armazenados estão bloqueados, você pode definir o thread para buscar os dados um pouco menos e não o definirei aqui.
Ainda é a mesma pergunta de antes. Agora deixe três threads executá -lo. Vamos dar uma olhada na pergunta:
Existem três threads, o thread infantil 1 executa 10 vezes primeiro, o encadeamento da criança 2 é executado 10 vezes, depois o encadeamento principal é executado 5 vezes e depois muda para a linha infantil 1 executa 10 vezes, o thread filho 2 é executado 10 vezes, o encadeamento principal é executado 5 vezes ... nesta viagem de ida e volta.
Se você não usa condição, é realmente difícil de fazer, mas é muito conveniente fazê -lo com a condição. O princípio é muito simples. Definir três condições. Depois que o thread infantil 1 é executado, o thread infantil 2 acorda o fio principal e o thread principal acorda o fio filho 1. O mecanismo de despertar é semelhante ao buffer acima. Vamos dar uma olhada no código abaixo, é fácil de entender.
classe public threeconditionCommunication {public static void main (string [] args) {Business Business = new Business (); new Thread (new Runnable () {// Abra um thread Child @Override public void run () {para (int i = 1; i <= 50; i ++) {Bussiness.Sub1 (i); {// Inicie outro thread infantil @Override public void run () {for (int i = 1; i <= 50; i ++) {Bussiness.sub2 (i);}}}). Start (); // Método principal thread principal para (int i = 1; i <= 50; i ++) {{BUSSINENT.Main (i); condição1 = Lock.NewCondition (); // condição é uma condição2 = Lock.NewCondition (); conditionMain = Lock.NewCondition (); private int BshouldSub = 0; public void sub1 (int i) {Lock.lock (); Try {while (Bshsub! {// TODO gerado automaticamente Blocke.printStackTrace ();}} para (int j = 1; j <= 10; j ++) {System.out.println ("Sub-1 Thread Sequence de" + J + ", loop de" + i);} bshouldsub = 1; condicion2.scnal; {lock.unlock();}}public void sub2(int i) {lock.lock();try {while (bShouldSub != 1) {try {condition2.await();//Use condition to call the await method}catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}for (int j = 1; j <= 10; j ++) {System.out.println ("Sub2 Thread Sequence de" + j + ", loop de" + i);} bshouldsub = 2; conditionMain.signal (); // Deixe o thread principal execute} finalmente {Lock.unlock ();}} void public (int i) {{ {conditionMain.await (); // Use a condição para chamar o método aguardado} catch (Exceção e) {// TODO GENERATO AUTOGERADO BLOCKE.PRINTSTACKTRACE ();}} para (int j = 1; j <= 5; j ++) {System.out.println ("Sequência de rosca principal de" + j + j ", J ++) {System.out.println (" Principal sequência de " + j + j", " 0; condition1.Signal (); // Deixe o thread 1 executar} finalmente {Lock.unlock ();}}}}O código parece um pouco longo, mas é uma ilusão e a lógica é muito simples. Isso é tudo para resumir a tecnologia de condição em threads.
O exposto acima é o conteúdo inteiro deste artigo sobre o exemplo de código do aplicativo da condição de concorrência Java Condição de bloqueio. Espero que seja útil para todos. Amigos interessados podem continuar se referindo a outros tópicos relacionados neste site. Se houver alguma falha, deixe uma mensagem para apontá -la. Obrigado amigos pelo seu apoio para este site!