Las colas administran los datos según el principio de primero en entrar, primero en salir. Si intenta agregar un elemento a una cola de bloqueo completa o eliminar un elemento de una cola de bloqueo vacía, el hilo se bloqueará. Las colas de bloqueo son una herramienta útil cuando cooperan varios subprocesos. El hilo de trabajo puede almacenar periódicamente resultados intermedios en la cola de bloqueo. Otros subprocesos de trabajo extraen los resultados intermedios y los modifican en el futuro. La cola equilibra automáticamente la carga. Si el primer conjunto de subprocesos se ejecuta más lento que el segundo conjunto, el segundo conjunto de subprocesos se bloquea mientras espera los resultados. Si el primer conjunto de subprocesos se ejecuta rápidamente, esperará a que el segundo conjunto de subprocesos se ponga al día.
El siguiente programa muestra cómo utilizar colas de bloqueo para controlar conjuntos de subprocesos. El programa busca todos los archivos en un directorio y todos sus subdirectorios e imprime una lista de archivos que contienen la palabra clave especificada.
El paquete java.util.concurrent proporciona 4 variantes de colas de bloqueo: LinkedBlockingQueue, ArrayBlockingQueue, PriorityBlockingQueue y DelayQueue. Usamos ArrayBlockingQueue. ArrayBlockingQueue requiere una capacidad determinada cuando se construye y, opcionalmente, puede requerir equidad. Si se establece el parámetro de equidad, el hilo con el tiempo de espera más largo se procesará primero. Generalmente, la equidad le costará rendimiento, así que úsela sólo cuando realmente la necesite.
El hilo productor enumera todos los archivos en todos los subdirectorios y los coloca en una cola de bloqueo. Esta operación es rápida y, si la cola no está limitada, pronto contendrá archivos que no se encontraron.
También iniciamos una gran cantidad de hilos de búsqueda. Cada hilo de búsqueda toma un archivo de la cola, lo abre, imprime todas las líneas que contienen la palabra clave y luego toma el siguiente archivo. Usamos un pequeño truco para matar el hilo una vez finalizado el trabajo. Para indicar que se ha completado, el hilo de enumeración coloca un objeto virtual en la cola. (Esto es similar a poner una bolsa virtual en un transportador de equipaje que dice "Última bolsa".) Cuando el hilo de búsqueda recupera el objeto virtual, lo devuelve y finaliza.
Tenga en cuenta que aquí no se requiere ninguna sincronización de subprocesos explícita. En este programa, utilizamos la estructura de datos de la cola como mecanismo de sincronización.
Copie el código de código de la siguiente manera:
importar java.io.*;
importar java.util.*;
importar java.util.concurrent.*;
clase pública BlockingQueueTest
{
principal vacío estático público (String [] argumentos)
{
Escáner en = nuevo escáner (System.in);
System.out.print("Ingrese el directorio base (por ejemplo, /usr/local/jdk1.6.0/src): ");
Directorio de cadenas = in.nextLine();
System.out.print("Ingrese la palabra clave (por ejemplo, volátil): ");
Palabra clave de cadena = in.nextLine();
finalint FILE_QUEUE_SIZE = 10;
finalint SEARCH_THREADS = 100;
Cola BlockingQueue<Archivo> = nueva ArrayBlockingQueue<Archivo>(FILE_QUEUE_SIZE);
Enumerador FileEnumerationTask = new FileEnumerationTask (cola, nuevo archivo (directorio));
nuevo hilo(enumerador).start();
para (int i = 1; i <= SEARCH_THREADS; i++)
new Thread(new SearchTask(cola, palabra clave)).start();
}
}
/**
* Esta tarea enumera todos los archivos en un directorio y sus subdirectorios.
*/
la clase FileEnumerationTask implementa Runnable
{
/**
* Construye una FileEnumerationTask.
* @param cola la cola de bloqueo a la que se agregan los archivos enumerados
* @param StartingDirectory el directorio en el que iniciar la enumeración
*/
public FileEnumerationTask(BlockingQueue<Archivo> cola, Directorio inicial de archivos)
{
this.queue = cola;
this.startingDirectory = directorio inicial;
}
ejecución pública vacía()
{
intentar
{
enumerar (directorio inicial);
cola.put(DUMMY);
}
captura (Excepción interrumpida e)
{
}
}
/**
* Enumera recursivamente todos los archivos en un directorio determinado y sus subdirectorios
* directorio @param el directorio en el que comenzar
*/
enumeración pública vacía (directorio de archivos) lanza InterruptedException
{
Archivo[] archivos = directorio.listFiles();
para (Archivo archivo: archivos)
{
if (file.isDirectory()) enumerar(archivo);
else cola.put(archivo);
}
}
Archivo estático público DUMMY = nuevo archivo ("");
cola privada BlockingQueue<Archivo>;
Directorio inicial de archivos privados;
}
/**
* Esta tarea busca archivos para una palabra clave determinada.
*/
clase SearchTask implementa Runnable
{
/**
* Construye una SearchTask.
* @param queue la cola de la que tomar archivos
* @param palabra clave la palabra clave a buscar
*/
SearchTask pública (cola BlockingQueue<Archivo>, palabra clave de cadena)
{
this.queue = cola;
this.palabra clave = palabra clave;
}
ejecución pública vacía()
{
intentar
{
booleano hecho = falso;
mientras (!hecho)
{
Archivo archivo = cola.take();
si (archivo == FileEnumerationTask.DUMMY)
{
cola.put(archivo);
hecho = verdadero;
}
de lo contrario buscar (archivo);
}
}
captura (IOException e)
{
e.printStackTrace();
}
captura (Excepción interrumpida e)
{
}
}
/**
* Busca en un archivo una palabra clave determinada e imprime todas las líneas coincidentes.
* @param file el archivo a buscar
*/
la búsqueda pública vacía (archivo de archivo) arroja IOException
{
Escáner en = nuevo escáner (nuevo FileInputStream (archivo));
int número de línea = 0;
mientras (en.hasNextLine())
{
número de línea++;
Línea de cadena = in.nextLine().trim();
if (line.contains(palabra clave)) System.out.printf("%s:%d %s%n", file.getPath(), lineNumber, line);
}
cercar();
}
cola privada BlockingQueue<Archivo>;
palabra clave de cadena privada;
}