986
Capítulo 23 Subprocesamiento múltiple
•
Las
instrucciones
synchronized
se declaran mediante la palabra clave
synchronized
:
synchronized
(
objeto
)
{
instrucciones
}
// fin de la instrucción synchronized
en
donde
objeto es el objeto cuyo bloqueo de monitor se va a adquirir; generalmente, objeto es
this
si es el objeto en
el que aparece la instrucción
synchronized
.
•
Un
método
synchronized
es equivalente a una instrucción
synchronized
que encierra el cuerpo completo de un
método, y que utiliza a
this
como el objeto cuyo bloqueo de monitor se va a adquirir.
•
La
interfaz
ExecutorService
proporciona el método
awaitTermination
para obligar a un programa a esperar a
que los subprocesos completen su ejecución. Este método devuelve el control al que lo llamó, ya sea cuando se com-
pleten todas las tareas que se ejecutan en el objeto
ExecutorService
, o cuando se agote el tiempo de inactividad
especifi cado. Si todas las tareas se completan antes de que se agote el tiempo de
awaitTermination
, este método
devuelve
true
; en caso contrario devuelve
false
. Los dos argumentos para
awaitTermination
representan un
valor de límite de tiempo y una unidad de medida especifi cada con una constante de la clase
TimeUnit
.
• Podemos simular la atomicidad al asegurar que sólo un subproceso lleve a cabo un conjunto de operaciones al
mismo tiempo. La atomicidad se puede lograr mediante el uso de la palabra clave
synchronized
para crear una
instrucción o método
synchronized
.
• Al compartir datos inmutables entre subprocesos, debemos declarar los campos de datos correspondientes como
final
, para indicar que los valores de las variables no cambiarán una vez que se inicialicen.
Sección 23.6 Relación productor/consumidor sin sincronización
• En una relación productor/consumidor con subprocesamiento múltiple, un subproceso productor genera los datos
y los coloca en un objeto compartido, llamado búfer. Un subproceso consumidor lee los datos del búfer.
• Las operaciones con los datos del búfer compartidos por un subproceso productor y un subproceso consumidor
son dependientes del estado; las operaciones deben proceder sólo si el búfer se encuentra en el estado correcto. Si el
búfer se encuentra en un estado en el que no esté completamente lleno, el productor puede producir; si el búfer se
encuentra en un estado en el que no esté completamente vacío, el consumidor puede consumir.
• Los subprocesos con acceso a un búfer deben sincronizarse para asegurar que lo datos se escriban en el búfer, o se
lean del búfer sólo si éste se encuentra en el estado apropiado. Si el productor que trata de colocar los siguientes datos
en el búfer determina que éste se encuentra lleno, el subproceso productor debe esperar hasta que haya espacio. Si
un subproceso consumidor encuentra el búfer vacío o que los datos anteriores ya se han leído, debe también esperar
hasta que haya nuevos datos disponibles.
Sección 23.7 Relación productor/consumidor:
ArrayBlockingQueue
• Java incluye una clase de búfer completamente implementada llamada
ArrayBlockingQueue
en el paquete
java.
util.concurrent
, que implementa a la interfaz
BlockingQueue
. Esta interfaz extiende a la interfaz
Queue
y declara
los métodos
put
y
take
, los equivalentes con bloqueo de los métodos
offer
y
poll
de
Queue
, respectivamente.
•
El
método
put
coloca un elemento al fi nal del objeto
BlockingQueue
, y espera si la cola está llena. El método
take
elimina un elemento de la parte inicial del objeto
BlockingQueue
, y espera si la cola está vacía. Estos métodos hacen
que la clase
ArrayBlockingQueue
sea una buena opción para implementar un búfer compartido. Debido a que el
método
put
bloquea hasta que haya espacio en el búfer para escribir datos, y el método take bloquea hasta que haya
nuevos datos para leer, el productor debe producir primero un valor, el consumidor sólo consume correctamente
hasta después de que el productor escribe un valor, y el productor produce correctamente el siguiente valor (después
del primero) sólo hasta que el consumidor lea el valor anterior (o primero).
•
ArrayBlockingQueue
almacena los datos compartidos en un arreglo. El tamaño de este arreglo se especifi ca como
argumento para el constructor de
ArrayBlockingQueue
. Una vez creado, un objeto
ArrayBlockingQueue
tiene su
tamaño fi jo y no se expandirá para dar cabida a más elementos.
Sección 23.8 Relación productor/consumidor con sincronización
• Usted puede implementar su propio búfer compartido, usando la palabra clave
synchronized
y los métodos
wait
,
notify
y
notifyAll
de
Object
, que pueden usarse con condiciones para hacer que los subprocesos esperen cuando
no puedan realizar sus tareas.
• Si un subproceso obtiene el bloqueo de monitor en un objeto, y después determina que no puede continuar con
su tarea en ese objeto sino hasta que se cumpla cierta condición, el subproceso puede llamar al método
wait
de
23_MAQ_CAP_23_DEITEL.indd986
4/19/081:34:03AM