O padrão do consumidor do produtor é o padrão mais comum entre a multi-threading: o thread do produtor (um ou mais) gera pão e o coloca na cesta (conjunto ou matriz) e, ao mesmo tempo, o tópico do consumidor (um ou mais) retira o pão da cesta (set ou matriz) e o consome. Embora tenham tarefas diferentes, os recursos que processam são os mesmos, o que reflete um método de comunicação entre thread.
Este artigo explicará primeiro a situação de produtores únicos e consumidores únicos e depois explicará a situação do modelo multiprodutor e multi-consumidor. Esses dois modos também serão implementados usando o mecanismo Wait ()/Nofity ()/Nofityall () e o mecanismo Lock ()/Unlock (), respectivamente.
Antes de iniciar a introdução do padrão, explique os detalhes de uso dos métodos wait (), notify () e notifyAll (), bem como o uso aprimorado de bloqueio ()/desbloqueio (), aguardar ()/signal ()/signalall ().
1. O princípio do mecanismo de espera e acordar
espera (), notify () e notifyAll () representam respectivamente threads que entram no sono, acorde o fio do sono e acorde todos os threads de dormir. Mas, qual tópico é o objeto? Além disso, todos os três métodos descritos na documentação da API devem ser usados sob a premissa de um monitor válido (que pode ser entendido como segurando um bloqueio). O que esses três métodos têm a ver com o bloqueio?
Tomar o código de sincronização Sincronizar (OBJ) {} ou as funções de sincronização como exemplo, wait (), notify () e notifyAll () podem ser usadas em sua estrutura de código, porque todas mantêm bloqueios.
Para os dois blocos de código de sincronização a seguir, o Lock OBJ1 e o Lock OBJ2 são usados, respectivamente. O thread 1 e o thread 2 executam o código de sincronização correspondente ao OBJ1, e Thread 3 e Thread 4 execute o código de sincronização correspondente ao OBJ2.
classe mylock implementa Runnable {public int sinaliza = 0; Objeto obj1 = new Object (); Objeto obj2 = new Object (); public void run () {while (true) {if (sinalizador%2 = 0) {sincronizado (obj1) {// Threads t1 e t2 execute esta tarefa de sincronização // tente {obj1.wait ();} catch (interruptException i) {} //obj1.notify () /bj1 (interruptException i) {} //obj1.notify () /bj1.n () () (); Sincronizado (obj2) {// Thread T3 e T4 Execute esta tarefa de sincronização // tente {obj2.wait ();} catch (interruptedException i) {} //obj2.notify () //obj2.NotifyAll ()}}}}}}}}}} = new MyLock (); Thread t1 = novo thread (ml); Tópico T2 = novo thread (ml); Thread t3 = novo thread (ml); Thread t4 = novo thread (ml); t1.start (); t2.start (); tente {thread.sleep (1)} catch (interruptedException i) {}; ml.flag ++; t3.start (); t4.start (); }}Quando o T1 começa a ser executado para esperar (), ele entra em um estado de sono, mas não é um sono normal, mas dorme em um pool de threads identificado por Obj1 (na verdade, o monitor corresponde ao pool de threads, mas o monitor e o bloqueio estão unidos neste momento). Quando o T2 começa a executar, ele descobre que o Lock OBJ1 é mantido por outros threads e entrará em um estado de sono. Desta vez, é porque o recurso de bloqueio está esperando em vez do sono inserido por espera (). Como o T2 já determinou que está se candidatando à trava Obj1, ele também entrará no sono do pool de threads Obj1, em vez do sono comum. Da mesma forma, T3 e T4, esses dois threads entrarão no pool de threads Obj2 para dormir.
Quando um thread é executado para notificar (), esse notify () acordará aleatoriamente qualquer encadeamento no pool de threads correspondente ao seu bloqueio. Por exemplo, obj1.Notify () acordará qualquer tópico para dormir no pool de threads Obj1 (é claro, se não houver um tópico para dormir, não faça nada). Da mesma forma, notifyAll () acorda todos os threads de dormir no pool de threads correspondentes da trava.
O que você deve descobrir é o "bloqueio correspondente", porque o bloqueio deve ser especificado explicitamente ao ligar para espera (), notify () e notifyAll (). Por exemplo, obj1.wait (). Se o bloqueio pertence a ele for omitido, significa esse objeto, ou seja, os prefixos desses três métodos só podem ser omitidos em funções de sincronização não estática.
Em suma, quando a sincronização é usada, a trava é usada e o encadeamento tem uma casa e toda a sua base é determinada pela trava pertencente. Por exemplo, quando a sincronização do encadeamento, ele determina se o bloqueio está ocioso para decidir se deve executar o código subsequente e também determina se deve ir a um pool de threads específico para dormir. Ao despertar, ele apenas acordará o fio no pool de threads correspondente à trava.
Na aplicação desses métodos, geralmente em uma tarefa, espera () e notify ()/notifyAll () aparecem em pares e execute um por um. Em outras palavras, durante esta rodada de execução síncrona atômica, espera -se () é executado para dormir ou notificar () é executado para acordar o fio do sono no pool de threads. Para alcançar a execução seletiva, você pode considerar o uso da marcação como base para o julgamento. Consulte os exemplos a seguir.
2. Lock e condição
Os três métodos da série Wait () são muito limitados porque as ações de sono e despertar são completamente acopladas à fechadura. Por exemplo, o encadeamento associado ao OBJ1 de bloqueio só pode acordar o encadeamento no pool Obj1 Thread, mas não pode acordar o fio associado ao obj2 de bloqueio; Por exemplo, quando a sincronização sincronizada foi originalmente sincronizada, o bloqueio foi implicitamente adquirido automaticamente quando a sincronização foi iniciada e, após a execução de toda a tarefa, ela implicitamente liberou o bloqueio, o que significa que a ação de adquirir o bloqueio e liberar o bloqueio não pode ser controlado manualmente.
A partir do JDK 1.5, o Java fornece o pacote java.util.concurrent.locks, que fornece a interface de bloqueio, a interface de condição e a interface ReadWritelock. As duas primeiras interfaces dissociam os métodos de trava e monitor (sono, operações de despertar). A interface de bloqueio fornece apenas bloqueios. Através do método de bloqueio newconditon (), um ou mais monitores associados ao bloqueio podem ser gerados. Cada monitor tem seus próprios métodos de sono e despertar. Em outras palavras, o bloqueio substitui o uso de métodos sincronizados e blocos de código sincronizado, e a condição substitui o uso de métodos de monitor de objeto.
Como mostrado na figura abaixo:
Quando um thread executa condicionado1.await (), o thread inserirá o pool de threads correspondente ao monitor da condição1 para dormir. Quando o condicional1.signal () é executado, qualquer encadeamento no conjunto de encadeamentos da condição1 será despertado aleatoriamente. Quando o condicional1.signalall () é executado, todos os threads no pool da condição1 de threads serão despertados. Da mesma forma, o mesmo é verdadeiro para o monitor da condição2.
Mesmo que existam vários monitores, desde que estejam associados ao mesmo objeto de bloqueio, o outro encadeamento poderá ser operado através do monitor. Por exemplo, um thread no condition1 pode executar o condicional2.Signal () para acordar um encadeamento no pool da condição2.
Para usar esse modo de associação de bloqueios e monitores, consulte as etapas a seguir:
importar java.util.concurrent.locks.*; bloquear l = new reentrantlock (); condição con1 = l.newcondition (); condição con2 = l.newcondition (); l.lock (); tentativa {// segmento de código contendo aguard (), sinalize () ou sinall () ...} finalmente {// // Como o segmento de código pode ser anormal, o desbloqueio () deve ser executado, tente ser usado e o desbloqueio () deve ser colocado no segmento finalmente}Para uso específico, consulte o código de exemplo para bloqueio e condição posteriormente.
3. Modelo de consumidor único de produtor único
Um tópico de produtor, um tópico de consumo. Para cada pão produzido pelo produtor, coloque -o no prato, o consumidor retira o pão do prato para consumo. A base para os produtores julgarem se continuarem a produção é que não há pão no prato, enquanto a base para os consumidores julgarem se consumir é que há pão no prato. Como neste modo, apenas um pão de pão é sempre colocado no prato, a placa pode ser omitida e o produtor e o consumidor podem entregar o pão passo a passo.
Primeiro, precisamos descrever essas três categorias: um é o recurso operado por vários threads (aqui é pão), o segundo é o produtor e o terceiro é o consumidor. No exemplo a seguir, encapsulei os métodos de produção de pão e consumo de pão nas classes produtoras e de consumidores, respectivamente, o que é mais fácil de entender se eles são encapsulados na classe de pão.
// Descrição Recurso: o nome e o número de pão, determinados pelo número de pão de pão pão {public string name; public int count = 1; bandeira booleana pública = false; // Esta marca fornece marcas de julgamento para espera () e notify ()} // o recurso de pão processado pelo produtor e pelo consumidor são os mesmos. Para garantir isso, // a aula de pão pode ser projetada de acordo com o padrão singleton, ou o mesmo objeto de pão pode ser transmitido ao produtor e ao consumidor através do método de construção. O último método é usado aqui. // Descreva o produtor produtor implementa Runnable {Private Bread B; // membro do produtor: o recurso que ele deseja processar o produtor (pão b) {this.b = b; } // fornecendo um método para produzir produtos de vazio público de pão (nome da string) {b.Name = nome + B.Count; B.Count ++; } public void run(){ while(true){ synchronized(Bread.class){ //Use Bread.class as the lock identifier so that the synchronized code blocks of producers and consumers can use the same lock if(b.flag){ //wait() must be inside the synchronous code block, not only because the lock must be held to sleep, but there will be confusion in the judgment of the lock resource tente {pão.class.wait ();} catch (interruptedException i) {}} Produce ("pão"); System.out.println (Thread.currentThread (). GetName ()+"---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // notify () também deve ser sincronizado; caso contrário, o bloqueio foi liberado e a ação de despertar não pode ser executada // ps: em uma tarefa de sincronização, espera () e notify () deve ser executado apenas, caso contrário, o tópico da outra parte será confundido}}}}} // Descreva o consumidor do consumidor Runnable {privado b; this.b = b; System.out.println (Thread.currentThread (). GetName ()+"---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Criar objetos de consumidor e aproveitar o mesmoO resultado final da execução deve ser produzido e consumido, e este é um ciclo contínuo. do seguinte modo:
Thread-0 --- Produtor ---- pão1Thread-1 --- Consumidor ------- pão1Thread-0 --- Produtor ---- pão2thread-1 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4. Use bloqueio e condição para realizar um modelo único de produção e consumo
O código é o seguinte:
importar java.util.concurrent.locks.*; classe pão {name public string; public int count = 1; bandeira booleana pública = false; // fornece o mesmo objeto de bloqueio e o mesmo objeto de condição para produtores e consumidores de bloqueio estático público = new reentrantlock (); condição estática pública Condição = Lock.NewCondition ();} Produtor de classe implementa Runnable {Private Bread B; Produtor (pão b) {this.b = b; } public void Produce (Nome da String) {B.Name = Nome + B.Count; B.Count ++; } public void run () {while (true) {// use pão.lock para bloquear o recurso pão.lock.lock (); tente {if (b.flag) {tente {pão.condition.await ();} catch (interruptedException i) {}} Produce ("pão"); System.out.println (Thread.currentThread (). GetName ()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Bread.Condition.Signal (); tente {if (! b.flag) {tente {pão.condition.await ();} catch (interruptedException i) {}} System.out.println (thread.currentThread (). GetName ()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Criar objetos de consumidor e aproveitar o mesmo5. Modelo de multi-produção e consumo (pão único)
Aqui, primeiro explicamos o modelo de vários produtores e vários consumidores, mas no máximo um pão ao mesmo tempo. Esse modelo pode não ser o ideal na realidade, mas, para levar à verdadeira modelo de produção e consumo múltiplo mais tarde, acho que é necessário explicar esse modelo aqui e analisar esse modelo e como ele evoluiu a partir do código de produção único e de consumo único.
Como mostrado na figura abaixo:
Desde a produção única e o consumo único até a produção múltipla e o consumo múltiplo, devido a problemas de segurança com vários threads e problemas de impasse, há dois problemas que precisam ser considerados:
Para uma parte, como a multi-threading pode alcançar a mesma capacidade de produção ou consumo que a fibra única? Em outras palavras, como fazer com que a multi-thread pareça única. A maior diferença entre threading e threading único são os problemas de segurança de threading. Portanto, desde que você garanta que as tarefas executadas pela multi-threading possam ser sincronizadas.
A primeira pergunta considera o problema de multi-threading em uma parte, e a segunda pergunta considera como as duas partes podem cooperar harmoniosamente para concluir a produção e o consumo. Ou seja, como garantir que um lado do produtor e do consumidor esteja dormindo enquanto o outro lado está ativo. Basta acordar a outra parte quando uma parte terminar de executar a tarefa de sincronização.
De fato, de um único thread a multi-threading, há dois problemas que precisam ser considerados: fora da sincronização e impasse. (1) Quando o produtor e o lado do consumidor têm multi-threads, os multi-threads do produtor podem ser considerados como um fio como um todo, e os múltiplos threads do lado do consumidor também como um todo, que resolve o problema de segurança do encadeamento. (2) Combinar toda a produção e todo o consumidor é considerado multi-threading para resolver o problema de impasse. A maneira de resolver o impasse em Java é acordar a outra festa ou acordar tudo.
A questão é como garantir a sincronização entre vários threads de uma determinada parte? O código de um único consumidor é analisado pela execução multithread como exemplo.
while (true) {sincronizado (pão.class) {if (! b.flag) {try {pão.class.wait ();} catch (interruptedException i) {}} System.out.println (thread.currentThread (). GetName ()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Suponha que o thread de consumo 1 acorde o thread de consumo 2 depois de consumir um pedaço de pão e continue a fazer loop, julgar se (! Bandeira), ele esperará e a fechadura for liberada. Supondo que a CPU apenas selecione o thread do consumidor 2, o thread do consumidor 2 também entrará no Wait. Quando o produtor produz um pão de pão, suponha que o thread de consumo 1 seja despertado, ele continuará consumindo o pão recém -produzido a partir da declaração de espera, suponha que o thread de consumo 2 seja despertado novamente. Quando o thread de consumo 2 é selecionado pela CPU, o thread de consumo 2 também consumirá para baixo a partir da declaração de espera, e o pão que acabou de ser produzido é consumido. O problema surge novamente. Os threads de consumo continuamente despertados 1 e 2 consomem o mesmo pão, o que significa que o pão é consumido repetidamente. Este é outro problema de sincronização ou síncel multithread.
Depois de falar sobre isso por um longo tempo, é realmente muito simples de analisar depois de aumentar a linha de visão. Enquanto os dois ou mais fios de uma parte aguardam o julgamento B.Flag, os dois ou mais threads poderão ser despertados continuamente e continuar a ser produzidos ou consumidos para baixo. Isso cria o problema da sincronização out-sincronização de múltiplos threads.
O problema da insegurança está no fato de que vários threads na mesma parte continuam a produzir ou consumir para baixo após o despertar contínuo. Isso é causado pela instrução IF. Se o thread de espera puder voltar para determinar se o B.Flag é verdadeiro após o despertar, ele pode fazer decidir se deve continuar esperando, produção ou consumo para baixo.
Você pode substituir a instrução IF por um tempo para atender aos requisitos. Dessa forma, independentemente de vários tópicos em uma determinada parte serem despertados continuamente, eles voltarão para julgar B.Flag.
while (true) {sincronizado (pão.class) {while (! b.flag) {try {pão.class.wait ();} catch (interruptedException i) {}} System.out.println (thread.currentThread (). GetName ()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------A primeira questão de segurança multithread foi resolvida, mas ocorreram problemas de impasse. Isso é fácil de analisar. O produtor é considerado como um todo e o consumidor também é um todo. Quando todos os tópicos do produtor estão esperando (os threads da festa de produção são despertados continuamente, todos os threads da festa esperarão) e o consumidor também está esperando, e o impasse aparecerá. De fato, se você olhar de maneira amplificada, o produtor e o consumidor serão considerados como um thread, respectivamente. Esses dois threads formam vários threads. Quando um lado espera e não pode acordar o outro lado, o outro lado definitivamente espera, para que seja um impasse.
Para o problema do impasse entre ambas as partes, desde que você garanta que a outra parte possa ser despertada, em vez do despertar contínuo de uma parte, ela pode ser resolvida. Basta usar o notifyAll () ou SignalAll (), ou você pode acordar o outro thread através do sinal () para resolver o problema. Veja o segundo código abaixo.
De acordo com a análise acima, se o código de produção única e o modelo de consumo único for aprimorado, ele poderá ser alterado para um modelo de pão único multi-produção e múltiplo-consumo.
// segmento de código 1 pão de classe {name public string; public int count = 1; bandeira booleana pública = false; } // Descreva o produtor produtor implementa Runnable {Private Bread B; Produtor (pão b) {this.b = b; } public void Produce (Nome da String) {B.Name = Nome + B.Count; B.Count ++; } public void run () {while (true) {synchronized (pão.class) {while (b.flag) {try {pão.class.wait ();} catch (interruptedException i) {}} Produce ("pão"); System.out.println (Thread.currentThread (). GetName ()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Consumo de string () {return b.name; System.out.println (Thread.currentThread (). GetName ()+"-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- }} classe pública ProductionConsume_5 {public static void main (String [] args) {// 1 2 Thread CON_T1 = THREAGEM COND);A seguir, é apresentado o código refaturado usando o bloqueio e o conditon, usando o Signal () para acordar o outro thread.
// segmento de código 2import java.util.concurrent.locks.*; Classe pão {name public string; public int count = 1; bandeira booleana pública = false; bloqueio estático público = new reentrantlock (); public static condition pro_con = Lock.newcondition (); condição estática pública con_con = lock.newcondition ();} // descreva o produtor produtor implementa Runnable {private pão b; Produtor (pão b) {this.b = b; } public void Produce (Nome da String) {B.Name = Nome + B.Count; B.Count ++; } public void run () {while (true) {pão.lock.lock (); tente {while (b.flag) {try {pão.pro_con.await ();} catch (interruptedException i) {}} Produce ("pão"); System.out.println (thread.currentThread (). GetName ()+"-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Pão.con_con.signal (); Pão.lock.lock (); System.out.println (thread.currentThread (). GetName ()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- //2. Create producer and consumer objects Producer pro = new Producer(b); Consumer con = new Consumer(b); //3. Create thread object Thread pro_t1 = new Thread(pro); Thread pro_t2 = new Thread(pro); Thread con_t1 = new Thread(con); Thread con_t2 = new Thread(con); pro_t1.start(); pro_t2.start(); con_t1.start(); con_t2.start ();Vamos resumir as questões de mais produção e mais consumo:
(1). A solução para a sincronização de múltiplos threading de uma determinada parte é usar enquanto (sinalizador) determina se a espera;
(2). A solução para o problema do impasse de ambas as partes é acordar a outra parte. Você pode usar o método notifyAll (), Signalall () ou Signal () do monitor da outra parte.
6. Mais modelos de produção e consumo
Existem vários threads produtores e vários threads de consumidores. O produtor coloca o pão produzido em uma cesta (conjunto ou matriz), e o consumidor tira o pão da cesta. A base para os produtores julgarem a produção contínua é que a cesta está cheia e a base para os consumidores julgarem o consumo contínuo é se a cesta está vazia. Além disso, quando o consumidor tira o pão, a posição correspondente fica vazia novamente e o produtor pode voltar e continuar a produção a partir da posição inicial da cesta, que pode ser alcançada redefinindo o ponteiro da cesta.
Nesse modelo, além de descrever produtores, consumidores e pão, também é necessário descrever o recipiente da cesta. Suponha que uma matriz seja usada como contêiner, toda vez que o produtor produz um, o ponteiro de produção muda para trás e, toda vez que o consumidor consome um, o ponteiro de consumo muda para trás.
O código é o seguinte: você pode consultar o código de exemplo fornecido na classe API-> Condição
importar java.util.concurrent.locks. // o tamanho da cesta (tamanho int) {arr = novo pão [tamanho]; } // o ponteiro de entrada e saída privado int in_ptr, out_ptr; // Quantos pães restam no cesto privado int esquerda; bloqueio privado de bloqueio = new reentrantlock (); condição privada Full = Lock.NewCondition (); condição privada vazia = bloqueio.newcondition (); // pão em cesto público vazio em () {Lock.lock (); tente {while (esquerda == arr.length) {try {Full.await ();} catch (interruptedException i) {i.printStackTrace ();}} arr [in_ptr] = novo pão ("mianbao", produtor.num ++); System.out.println ("Coloque o pão:"+arr [in_ptr] .getName ()+"------- em cesto ["+in_ptr+"]"); Esquerda ++; if (++ in_ptr == arr.length) {in_ptr = 0;} empty.signal (); } finalmente {Lock.unlock (); }} // pão fora do cesto public pão () {Lock.lock (); tente {while (esquerda == 0) {try {empty.await ();} catch (interruptedException i) {i.printStacktrace ();}} pão out_bread = arr [out_ptr]; System.out.println ("Get the Bread:"+out_bread.getName ()+"---------- From Basket ["+out_ptr+"]"); esquerda--; if (++ out_ptr == arr.length) {out_ptr = 0;} full.signal (); retornar fora_bread; } finalmente {Lock.unlock (); }}} classe pão {nome da string privada; Pão (nome da corda, int num) {this.name = nome + num; } public string getName () {return this.name; }} Classe Produtor implementa Runnable {Private Basket Basket; public static int num = 1; // o primeiro número do produtor de nomes do pão (cesta b) {this.basket = b; } public void run () {while (true) {besta.in (); tente {thread.sleep (10);} catch (interruptedException i) {}}}} classe de classe implementa Runnable {private Basket Basket; pão privado i_get; Consumidor (cesto b) {this.basket = b; } public void run () {while (true) {i_get = besta.out (); tente {thread.sleep (10);} catch (interruptedException i) {}}}} public class Producnsume_7 {public static void main (string [] args) {cesto b = new cesta (20); // o tamanho da cesta = 20 produtor pro = novo produtor (b); Consumidor con = novo consumidor (b); Thread pro_t1 = novo thread (pro); Thread pro_t2 = novo thread (pro); Thread con_t1 = novo thread (con); Thread con_t2 = novo thread (con); Thread con_t3 = novo thread (con); pro_t1.start (); pro_t2.start (); con_t1.start (); con_t2.start (); con_t3.start (); }}Isso envolve consumidores, produtores, pão e cestas, onde pão e cestas são recursos operados por vários tópicos. O fio do produtor produz pão e o coloca na cesta, e o fio do consumidor retira o pão da cesta. O código ideal é encapsular tarefas de produção e tarefas de consumo na classe de recursos. Como o pão é um elemento do recipiente da cesta, ele não é adequado para empacotar a classe de pão e a embalagem na cesta facilita a operação do contêiner.
Observe que você deve colocar todos os códigos que envolvam operações de recursos dentro da trava, caso contrário, ocorrerá um problema de sincronização de saída com vários threads. Por exemplo, o método que produz pão é definido na classe do produtor e, em seguida, é usado como um parâmetro para a cesta do método.in () colocado na cesta, isto é, cesto.in (produtor ()), que é um comportamento errado porque o produtor () é passado para o método in () após a execução da trava.
O artigo acima é baseado no Java Producer and Consumer Model (Análise detalhada) e é o conteúdo inteiro compartilhado pelo editor. Espero que possa lhe dar uma referência e espero que você possa apoiar mais o wulin.com.