El patrón de consumidor del productor es el patrón más común entre el hilo múltiple: el hilo del productor (uno o más) genera pan y lo coloca en la canasta (set o matriz), y al mismo tiempo, el hilo de consumo (uno o más) saca el pan de la canasta (conjunto o matriz) y lo consume. Aunque tienen tareas diferentes, los recursos que procesan son los mismos, lo que refleja un método de comunicación entre hilos.
Este artículo explicará primero la situación de los productores individuales y los consumidores individuales, y luego explicará la situación del modelo de múltiples productores y múltiples consumidores. Estos dos modos también se implementarán utilizando el mecanismo Wait ()/nofity ()/nofityall () y el mecanismo Lock ()/desbloqueo () respectivamente.
Antes de comenzar la introducción del patrón, explique los detalles de uso de los métodos Wait (), Notify () y NotifyAll (), así como el uso mejorado de Lock ()/Unlock (), Await ()/Signal ()/Signalall ().
1. El principio del mecanismo de espera y despierta
Wait (), notify () y notifyall () represente respectivamente los hilos que entran en el sueño, despiertan el hilo de sueño y despertan todos los hilos para dormir. Pero, ¿qué hilo es el objeto? Además, los tres métodos descritos en la documentación de la API deben usarse bajo la premisa de un monitor válido (que puede entenderse como manteniendo un bloqueo). ¿Qué tienen que ver estos tres métodos con el bloqueo?
Tomar el bloque de código de sincronización sincronizado (obj) {} o las funciones de sincronización como ejemplo, Wait (), notify () y notifyall () se pueden usar en su estructura de código porque todos contienen bloqueos.
Para los siguientes dos bloques de código de sincronización, el bloqueo OBJ1 y el bloqueo OBJ2 se utilizan respectivamente. El subhicho 1 y el hilo 2 ejecuta el código de sincronización correspondiente a OBJ1, y el subproceso 3 y el subproceso 4 Ejecute el código de sincronización correspondiente a OBJ2.
clase myLock implementa runnable {public int flag = 0; Objeco obj1 = nuevo objeto (); Objero obj2 = nuevo objeto (); public void run () {while (true) {if (flag%2 = 0) {sincronizado (obj1) {// hilos t1 y t2 realizan esta tarea de sincronización // try {obj1.wait ();} capt (interruptedException i) {} //obj1.notify () //obj1.notifyall ()}}}} más sincronizado (obj2) {// hilo t3 y t4 realiza esta tarea de sincronización // try {obj2.wait ();} capt (interruptedException i) {} //obj2.notify () //obj2.notifyall ()}}}}}}}} class {public static static (string (string (string (string [string principalmente nuevo mylock (); Hilo t1 = nuevo hilo (ml); Hilo t2 = nuevo hilo (ml); Hilo t3 = nuevo hilo (ml); Hilo t4 = nuevo hilo (ml); t1.start (); t2.start (); Pruebe {Thread.sleep (1)} Catch (InterruptedException i) {}; ml.flag ++; t3.start (); t4.Start (); }}Cuando T1 comienza a ejecutarse para esperar (), entrará en un estado de sueño, pero no es un sueño normal, pero duerme en una piscina de hilo identificada por OBJ1 (en realidad, el monitor corresponde a la piscina de subprocesos, pero el monitor y el bloqueo están unidos en este momento). Cuando T2 comienza a ejecutarse, encuentra que Lock OBJ1 está sostenido por otros hilos y entrará en un estado de sueño. Esta vez es porque el recurso de bloqueo está esperando en lugar del sueño ingresado por Wait (). Debido a que T2 ya ha determinado que está solicitando el bloqueo OBJ1, también entrará en el sueño de la piscina de hilo OBJ1, en lugar de un sueño ordinario. Del mismo modo, T3 y T4, estos dos hilos entrarán en la piscina de subprocesos OBJ2 para dormir.
Cuando un subproceso se ejecuta para notificar (), esta notificación () despertará aleatoriamente cualquier hilo en el grupo de subprocesos correspondiente a su bloqueo. Por ejemplo, obj1.notify () despertará cualquier hilo para dormir en la piscina de hilo OBJ1 (por supuesto, si no hay hilo para dormir, no hagas nada). Del mismo modo, notifyall () despierta todos los hilos para dormir en el grupo de hilo correspondiente de la cerradura.
Lo que debe averiguar es el "bloqueo correspondiente", porque el bloqueo debe especificarse explícitamente al llamar a Wait (), notificar () y notifyAll (). Por ejemplo, obj1.wait (). Si el bloqueo pertenece a él, se omite, significa este objeto, es decir, los prefijos de estos tres métodos solo se pueden omitir en funciones de sincronización no estatales.
En resumen, cuando se usa la sincronización, se usa el bloqueo y el hilo tiene un hogar, y toda su base está determinada por el bloqueo de pertenencia. Por ejemplo, cuando la sincronización de subprocesos, determina si el bloqueo está inactivo para decidir si ejecutar el código posterior, y también determina si ir a una piscina de hilo específica para dormir. Al despertar, solo despertará el hilo en el grupo de hilo correspondiente al bloqueo.
En la aplicación de estos métodos, generalmente en una tarea, espera () y notify ()/notifyall () aparece en pares y ejecuta uno por uno. En otras palabras, durante esta ronda de ejecución sincrónica atómica, Wait () se ejecuta para dormir o notifica () se ejecuta para despertar el hilo de sueño en la piscina de hilo. Para lograr una ejecución selectiva, puede considerar usar el marcado como base para el juicio. Consulte los siguientes ejemplos.
2.lock y condición
Los tres métodos de la serie Wait () son muy limitados porque tanto las acciones de sueño como de despertar están completamente junto con el bloqueo. Por ejemplo, el hilo asociado con el bloqueo OBJ1 solo puede despertar el hilo en el grupo de hilo OBJ1, pero no puede despertar el hilo asociado con el bloqueo OBJ2; Por ejemplo, cuando la sincronización sincronizada se sincronizó originalmente, el bloqueo se adquirió implícitamente automáticamente cuando comenzó la sincronización, y después de ejecutarse toda la tarea, implícitamente lanzó automáticamente el bloqueo, lo que significa que la acción de adquirir el bloqueo y liberar el bloqueo no se pudo controlar manualmente.
A partir de JDK 1.5, Java proporciona el paquete java.util.concurrent.locks, que proporciona la interfaz de bloqueo, la interfaz de condición y la interfaz ReadWriteLock. Las dos primeras interfaces desacoplan los métodos de bloqueo y monitor (operaciones de sueño, operaciones de atención). La interfaz de bloqueo solo proporciona bloqueos. A través del método de bloqueo Newconditon (), se pueden generar uno o más monitores asociados con el bloqueo. Cada monitor tiene sus propios métodos de sueño y atención. En otras palabras, el bloqueo reemplaza el uso de métodos sincronizados y bloques de código sincronizados, y la condición reemplaza el uso de métodos de monitor de objetos.
Como se muestra en la figura a continuación:
Cuando un subproceso ejecuta condición1.await (), el hilo ingresará al grupo de subprocesos correspondiente al monitor de condición1 para dormir. Cuando la condición1.signal () se ejecuta, cualquier hilo en el grupo de subprocesos de condición1 se despertará aleatoriamente. Cuando se ejecuta la condición1.signAlall (), se despertarán todos los hilos en el grupo de subprocesos de condición1. Del mismo modo, lo mismo es cierto para el monitor de condición2.
Incluso si hay múltiples monitores, siempre que estén asociados con el mismo objeto de bloqueo, el otro hilo puede operarse en todo el monitor. Por ejemplo, un hilo en la condición1 puede ejecutar condición2.signal () para despertar un hilo en el grupo de subprocesos de condición2.
Para utilizar esta forma de asociación de cerraduras y monitores, consulte los siguientes pasos:
import java.util.concurrent.locks.*; bloqueo l = new ReentrantLock (); condición Con1 = l.newCondition (); condición Con2 = l.newCondition (); l.lock (); try {// Code segmento que contiene Agait (), señal () o señalall () ...} finalmente {l.unlock (); // Dado que el segmento de código puede ser anormal, el desbloqueo () debe ejecutarse, intento debe usarse y desbloquear () debe colocarse en el segmento finalmente}Para un uso específico, consulte el código de ejemplo para el bloqueo y la condición más adelante.
3. Modelo de consumo único de productor único
Un hilo de productor, un hilo del consumidor. Para cada pan producido por el productor, colóquelo en el plato, el consumidor saca el pan del plato para el consumo. La base para que los productores juzguen si continuar la producción es que no hay pan en el plato, mientras que la base para que los consumidores juzguen si consumir es que hay pan en el plato. Dado que en este modo, solo se coloca una barra de pan en el plato, el plato se puede omitir y el productor y el consumidor pueden entregar el pan paso a paso.
Primero, necesitamos describir estas tres categorías: una es el recurso operado por múltiples hilos (aquí está el pan), el segundo es el productor y el tercero es el consumidor. En el siguiente ejemplo, encapsula los métodos para producir pan y consumir pan en las clases de productor y consumidor, respectivamente, lo que es más fácil de entender si están encapsulados en la clase de pan.
// Descripción Recurso: el nombre y el número de pan, determinado por el número de pan de la clase de pan {nombre de cadena pública; público int count = 1; Public Boolean Flag = false; // Esta marca proporciona marcas de juicio para Wait () y notify ()} // El recurso de pan procesado por el productor y el consumidor son los mismos. Para garantizar esto, // la clase de pan se puede diseñar de acuerdo con el patrón singleton, o el mismo objeto de pan se puede pasar al productor y al consumidor a través del método de construcción. El último método se usa aquí. // Describe la clase de productor implementa Runnable {Private Bread B; // miembro del productor: el recurso que quiere procesar el productor (pan b) {this.b = b; } // Proporcionar un método para producir pan publicitario público void (nombre de cadena) {b.name = name + b.count; B.Count ++; } public void run () {while (true) {sincronizado (bread.class) {// use bread.class como identificador de bloqueo para que los bloques de código sincronizados de los productores y los consumidores puedan usar el mismo bloqueo si (b.flag) {// espera () dentro del bloque de código sincrón Pruebe {Bread.class.Wait ();} Catch (InterruptedException i) {}} producir ("pan"); System.out.println (thread.currentThread (). GetName ()+"----------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------- // notificar () también debe sincronizarse, de lo contrario, el bloqueo se ha lanzado, y la acción de despertar no se puede realizar // PS: en una tarea de sincronización, espera () y notify () solo debe ejecutarse, de lo contrario, el hilo de la otra parte se confundirá}}}} // describe el consumidor de la clase de consumo B) this.b = B; System.out.println (thread.currentThread (). GetName ()+"--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- // 2.El resultado de la ejecución final debe producirse y consumirse, y este es un ciclo continuo. como sigue:
Thread-0---Producer----Bread1Thread-1---Consumer-------Bread1Thread-0---Producer----Bread2Thread-1--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------
4. Use el bloqueo y la condición para realizar un modelo de producción y consumo
El código es el siguiente:
import java.util.concurrent.locks.*; class Bread {public String Name; público int count = 1; Public Boolean Flag = false; // Proporcione el mismo objeto de bloqueo y el mismo objeto de condición para los productores y consumidores bloqueo de bloqueo estático público = new ReentrantLock (); condición estática pública condición = lock.newcondition ();} El productor de clase implementa runnable {Pan privado B; Productor (pan b) {this.b = b; } public void product (nombre de cadena) {b.name = name + b.count; B.Count ++; } public void run () {while (true) {// use Bread.lock para bloquear el recurso Bread.lock.lock (); Pruebe {if (b.flag) {try {bread.condition.await ();} capt (interruptedException i) {}} producir ("pan"); System.out.println (thread.currentThread (). GetName ()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------- Bread.condition.signal (); if (! b.flag) {try {bread.condition.await ();} capt (interruptedException i) {}} System.out.println(Thread.currentThread().getName()+"-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------- // 2.5. Modelo de múltiples producciones y consumo (pan único)
Aquí explicamos primero el modelo de múltiples productores y múltiples consumidores, pero a lo sumo un pan al mismo tiempo. Este modelo puede no ser ideal en la realidad, pero para conducir al modelo real de producción múltiple y consumo múltiple más tarde, creo que es necesario explicar este modelo aquí y analizar este modelo y cómo evolucionó a partir del código de producción única y consumo único.
Como se muestra en la figura a continuación:
Desde la producción única y el consumo único hasta la producción múltiple y el consumo múltiple, debido a problemas de seguridad de múltiples subprocesos y problemas de bloqueo muerto, hay dos problemas que deben considerarse:
Para una de las partes, ¿cómo puede lograr la misma capacidad de producción o consumo que la capacidad de producción que un solo subproceso? En otras palabras, cómo hacer un aspecto de subproceso múltiple de un solo hilo. La mayor diferencia entre el subproceso múltiple y el subproceso único son los problemas de seguridad de múltiples subprocesos. Por lo tanto, siempre que se asegure de que las tareas ejecutadas por múltiples subprocesos se puedan sincronizar.
La primera pregunta considera el problema de múltiples subprocesos en una parte, y la segunda pregunta considera cómo las dos partes pueden cooperar armoniosamente para completar la producción y el consumo. Es decir, cómo garantizar que un lado del productor y el consumidor duerman mientras que el otro lado está activo. Simplemente despierta a la otra parte cuando una de las partes haya terminado de realizar la tarea de sincronización.
De hecho, desde un solo hilo hasta múltiples subprocesos, hay dos problemas que deben considerarse: fuera de la sincronización y punto muerto. (1) Cuando tanto el productor como el lado del consumidor tienen múltiples subprocesos, los múltiples subprocesos del productor pueden considerarse como un hilo en su conjunto, y los hilos múltiples del lado del consumidor también en su conjunto, lo que resuelve el problema de seguridad del hilo. (2) Combinar toda la producción y todo el consumidor se considera como múltiples subprocesos para resolver el problema de punto muerto. La forma de resolver el punto muerto en Java es despertar a la otra fiesta o despertar todo.
La pregunta es cómo garantizar la sincronización entre múltiples hilos de una determinada parte. El código de un solo consumidor se analiza mediante la ejecución multiproceso como ejemplo.
while (true) {sincronizado (pan.class) {if (! b.flag) {try {bread.class.wait ();} capt (interruptedexception i) {}} System.out.println (thread.currentThread (). GetName ()+"--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------Supongamos que el hilo de consumo 1 despierta el hilo de consumo 2 después de consumir una barra de pan, y continúa enrollando, juzgar si (! Flag), esperará y se libera el bloqueo. Suponiendo que la CPU solo selecciona el Subproceso de Consumidor 2, el Subproceso de Consumidor 2 también ingresará a Wait. Cuando el productor produce una barra de pan, suponga que el hilo de consumo 1 se despierta, continuará consumiendo el pan recién producido de la declaración de espera, supongamos que el hilo de consumo 2 se despierta nuevamente. Cuando el hilo de consumo 2 es seleccionado por la CPU, el hilo de consumo 2 también consumirá a la baja de la declaración de espera, y el pan que se acaba de producir se consume. El problema surge nuevamente. Los hilos de consumo continuamente despertados 1 y 2 consumen el mismo pan, lo que significa que el pan se consume repetidamente. Este es otro problema de sincronización fuera de múltiples hilos.
Después de hablar de ello durante mucho tiempo, en realidad es muy simple de analizar después de ampliar la línea de visión. Mientras los dos o más hilos de una parte esperen el juicio B.Flag, entonces los dos o más hilos pueden despertarse continuamente y continuar siendo producidos o consumidos hacia abajo. Esto crea el problema de la sincronización fuera del subproceso múltiple.
El problema de la inseguridad radica en el hecho de que múltiples hilos en la misma parte continúan produciendo o consumiendo hacia abajo después del despertar continuo. Esto es causado por la declaración IF. Si el hilo de espera puede volver para determinar si B.Flag es cierto después de la activación, puede hacer que decida si continúa esperando, producción o consumo a la baja.
Puede reemplazar la declaración IF con una declaración de tiempo para cumplir con los requisitos. De esta manera, independientemente de si múltiples hilos en una determinada parte se despiertan continuamente, volverán al juez B.Flag.
while (true) {sincronizado (pan.class) {while (! b.flag) {try {bread.class.wait ();} capt (interruptedexception i) {}} System.out.println (thread.currentThread (). GetName ()+"--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------Se resolvió el primer problema de seguridad multiproceso, pero ocurrieron problemas de punto muerto. Esto es fácil de analizar. El productor es considerado como un todo y el consumidor también es un todo. Cuando todos los hilos del productor están esperando (los hilos de la fiesta de producción se despiertan continuamente, todos los hilos de la fiesta esperarán), y el consumidor también está esperando, y aparecerá el punto muerto. De hecho, si lo mira de manera amplificada, el productor y el consumidor son considerados como un hilo respectivamente. Estos dos hilos forman múltiples hilos. Cuando un lado espera y no puede despertar al otro lado, el otro lado definitivamente esperará, por lo que estará bloqueado.
Para el problema del punto muerto entre ambas partes, siempre que se asegure de que la otra parte pueda despertarse, en lugar del despertar continuo de la parte, se puede resolver. Simplemente use notifyall () o señalall (), o puede despertar el otro hilo a través de la señal () para resolver el problema. Vea el segundo código a continuación.
Según el análisis anterior, si se mejora el código de producción única y el modelo de consumo único, se puede cambiar a un modelo de pan único de múltiples producciones y consumo múltiple.
// Segmento de código 1 Bread de clase {nombre de cadena public; público int count = 1; Public Boolean Flag = false; } // Describe la clase del productor implementa Runnable {Private Bread B; Productor (pan b) {this.b = b; } public void product (nombre de cadena) {b.name = name + b.count; B.Count ++; } public void run () {while (true) {sincronizado (pan.class) {while (b.flag) {try {bread.class.wait ();} capt (interruptedexception i) {}} producir ("pan"); System.out.println (thread.currentThread (). GetName ()+"--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- Consumo de cadena () {return b.name; System.out.println (thread.currentThread (). GetName ()+"--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------- }} PublicConsume_5 {Public static void main (String [] args) {// 1. 2 hilo con_t1 = nuevo hilo (con);El siguiente es el código refactorizado usando Lock and Conditon, usando Signal () para despertar el otro hilo.
// segmento de código 2Import java.util.concurrent.locks.*; Class Bread {public String Name; público int count = 1; Public Boolean Flag = false; Lock Public Static Lock = New ReentrantLock (); condición estática pública pro_con = Lock.NewCondition (); Condición estática pública con_con = Lock.newCondition ();} // Describe la clase del productor implementa Runnable {PRIVADO PAN B; Productor (pan b) {this.b = b; } public void product (nombre de cadena) {b.name = name + b.count; B.Count ++; } public void run () {while (true) {bread.lock.lock (); Pruebe {while (b.flag) {try {bread.pro_con.await ();} capt (interruptedException i) {}} producir ("pan"); System.out.println (thread.currentThread (). GetName ()+"------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- Bread.con_con.signal (); while (! b.flag) {try {bread.con_con.await ();} capt (interruptedException i) {}} 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 ();Resumamos los problemas de más producción y más consumo:
(1). La solución a la sincronización de múltiples subprocesos de una determinada parte es usar mientras (indicador) para determinar si espera;
(2). La solución al problema de punto muerto de ambas partes es despertar a la otra parte. Puede usar notifyAll (), señalall () o el método Signal () del monitor de la otra parte.
6. Más modelos de producción y consumo
Hay múltiples hilos de productores y múltiples hilos de consumo. El productor pone el pan producido en una canasta (conjunto o matriz), y el consumidor saca el pan de la canasta. La base para que los productores juzguen la producción continua es que la canasta está llena, y la base para que los consumidores juzguen el consumo continuo es si la canasta está vacía. Además, cuando el consumidor saca el pan, la posición correspondiente se vuelve vacía nuevamente, y el productor puede retroceder y continuar la producción desde la posición inicial de la canasta, que se puede lograr restableciendo el puntero de la canasta.
En este modelo, además de describir a los productores, consumidores y pan, también es necesario describir el contenedor de canasta. Supongamos que una matriz se usa como contenedor, cada vez que el productor produce uno, el puntero de producción cambia hacia atrás, y cada vez que el consumidor consume uno, el puntero de consumo cambia hacia atrás.
El código es el siguiente: puede consultar el código de ejemplo dado en la clase API-> Condición
import java.util.concurrent.locks.*; canasta de clases {panre privado [] arr; // el tamaño de la cesta de cesta (intize int) {arr = new Bread [size]; } // El puntero de In y Out Private int in_ptr, out_ptr; // cuántos panes quedan en la canasta private int; bloqueo de bloqueo privado = nuevo reentrantlock (); condición privada full = lock.newcondition (); condición privada vacía = Lock.NewCondition (); // pan en canasta public void in () {Lock.lock (); Pruebe {while (left == arr.length) {try {full.await ();} catch (InterruptedException i) {I.PrintStackTrace ();}} arr [in_ptr] = new Bread ("Mianbao", productor.num ++); System.out.println ("Pon el pan:"+arr [in_ptr] .getName ()+"------- en la canasta ["+in_ptr+"]"); izquierda ++; if (++ in_ptr == arr.length) {in_ptr = 0;} vacía.signal (); } Finalmente {Lock.unlock (); }} // Bread de Basket Public Bread out () {Lock.lock (); Pruebe {while (left == 0) {try {vacía.await ();} capt (interruptedException i) {i.printStackTrace ();}} pan_bread = arr [out_ptr]; System.out.println ("Obtener el pan:"+out_bread.getName ()+"---------- desde la canasta ["+out_ptr+"]"); izquierda--; if (++ out_ptr == arr.length) {out_ptr = 0;} full.signal (); return out_bread; } Finalmente {Lock.unlock (); }}} Class Bread {Nombre de cadena privada; Pan (nombre de cadena, int num) {this.name = name + num; } public String getName () {return this.name; }} El productor de clase implementa Runnable {Cesta de cesta privada; Public static int num = 1; // El primer número para el productor de nombre de Bread (Baseta b) {this.basket = b; } public void run () {while (true) {best.in (); Pruebe {Thread.sleep (10);} Catch (InterruptedException i) {}}}} Class Consumer Implements Runnable {private Basket Basket; pan privado i_get; Consumer (Canasta B) {this.basket = b; } public void run () {while (true) {i_get = best.out (); Pruebe {Thread.sleep (10);} Catch (InterruptedException i) {}}}} public class ProduceConsume_7 {public static void main (String [] args) {Basket b = New Basket (20); // El tamaño de la canasta = 20 Productor Pro = nuevo productor (b); Consumer Con = nuevo consumidor (b); Thread pro_t1 = new Thread (pro); Thread pro_t2 = new Thread (pro); Hilo con_t1 = nuevo hilo (con); Hilo con_t2 = nuevo hilo (con); Hilo con_t3 = nuevo hilo (con); pro_t1.start (); pro_t2.start (); con_t1.start (); con_t2.start (); con_t3.start (); }}Esto involucra a los consumidores, productores, pan y canastas, donde el pan y las canastas son recursos operados por múltiples hilos. El hilo del productor produce pan y lo coloca en la canasta, y el hilo del consumidor saca el pan de la canasta. El código ideal es encapsular las tareas de producción y las tareas de consumo en la clase de recursos. Debido a que el pan es un elemento del recipiente de canasta, no es adecuado para empacar en la clase de pan, y el empaquetado en la canasta hace que sea más fácil operar el contenedor.
Tenga en cuenta que debe colocar todos los códigos que involucran operaciones de recursos dentro del bloqueo, de lo contrario, se producirá un problema de sincronización fuera de subproceso múltiple. Por ejemplo, el método que produce pan se define en la clase de productor, y luego se usa como un parámetro para el método canasta.in () colocado en la canasta, es decir, bank.in (productor ()), que es un comportamiento incorrecto porque el productor () se pasa al método in () después de que se ejecuta fuera del bloqueo.
El artículo anterior se basa en el modelo de productor y consumidor de Java (análisis detallado) y es todo el contenido compartido por el editor. Espero que pueda darle una referencia y espero que pueda apoyar más a Wulin.com.