Les files d'attente gèrent les données selon le principe du premier entré, premier sorti. Si vous essayez d'ajouter un élément à une file d'attente de blocage complète ou de supprimer un élément d'une file d'attente de blocage vide, le thread se bloquera. Les files d'attente de blocage sont un outil utile lorsque plusieurs threads coopèrent. Le thread de travail peut stocker périodiquement les résultats intermédiaires dans la file d'attente de blocage. D'autres threads de travail extraient les résultats intermédiaires et les modifient à l'avenir. La file d'attente équilibre automatiquement la charge. Si le premier ensemble de threads s'exécute plus lentement que le deuxième ensemble, le deuxième ensemble de threads se bloque en attendant les résultats. Si le premier ensemble de threads s'exécute rapidement, il attendra que le deuxième ensemble de threads rattrape son retard.
Le programme suivant montre comment utiliser les files d'attente de blocage pour contrôler des ensembles de threads. Le programme recherche tous les fichiers d'un répertoire et de tous ses sous-répertoires et imprime une liste de fichiers contenant le mot-clé spécifié.
Le package java.util.concurrent fournit 4 variantes de files d'attente de blocage : LinkedBlockingQueue, ArrayBlockingQueue, PriorityBlockingQueue et DelayQueue. Nous utilisons ArrayBlockingQueue. ArrayBlockingQueue nécessite une capacité donnée lors de sa construction et peut éventuellement exiger l'équité. Si le paramètre d'équité est défini, le thread avec le temps d'attente le plus long sera traité en premier. Généralement, l’équité vous coûtera en performance, alors utilisez-la uniquement lorsque vous en avez vraiment besoin.
Le thread producteur énumère tous les fichiers de tous les sous-répertoires et les place dans une file d'attente de blocage. Cette opération est rapide, et si la file d'attente n'est pas bouchée, elle contiendra bientôt des fichiers introuvables.
Nous avons également lancé un grand nombre de fils de recherche. Chaque fil de recherche prend un fichier de la file d'attente, l'ouvre, imprime toutes les lignes contenant le mot-clé, puis prend le fichier suivant. Nous utilisons une petite astuce pour tuer le fil une fois le travail terminé. Pour signaler l'achèvement, le thread d'énumération place un objet virtuel dans la file d'attente. (Cela revient à placer un sac virtuel sur un convoyeur à bagages indiquant « Dernier sac ».) Lorsque le fil de recherche récupère l'objet virtuel, il le remet en place et se termine.
Notez qu'aucune synchronisation explicite des threads n'est requise ici. Dans ce programme, nous utilisons la structure de données de file d'attente comme mécanisme de synchronisation.
Copiez le code comme suit :
importer java.io.* ;
importer java.util.* ;
importer java.util.concurrent.* ;
classe publique BlockingQueueTest
{
public static void main (String[] arguments)
{
Scanner dans = nouveau scanner (System.in);
System.out.print("Entrez le répertoire de base (par exemple /usr/local/jdk1.6.0/src) : ");
Répertoire de chaînes = in.nextLine();
System.out.print("Entrez le mot clé (par exemple volatile): ");
Mot-clé de chaîne = in.nextLine();
final int FILE_QUEUE_SIZE = 10 ;
final entier SEARCH_THREADS = 100 ;
BlockingQueue<File> queue = new ArrayBlockingQueue<File>(FILE_QUEUE_SIZE);
Enumérateur FileEnumerationTask = new FileEnumerationTask (file d'attente, nouveau fichier (répertoire));
new Thread(énumérateur).start();
pour (int i = 1; i <= SEARCH_THREADS; i++)
new Thread(new SearchTask(file d'attente, mot-clé)).start();
}
}
/**
* Cette tâche énumère tous les fichiers d'un répertoire et de ses sous-répertoires.
*/
la classe FileEnumerationTask implémente Runnable
{
/**
* Construit une FileEnumerationTask.
* @param queue la file d'attente de blocage à laquelle les fichiers énumérés sont ajoutés
* @param StartingDirectory le répertoire dans lequel démarrer l'énumération
*/
public FileEnumerationTask (file d'attente BlockingQueue<File>, File StartingDirectory)
{
this.queue = file d'attente ;
this.startingDirectory = startupDirectory ;
}
exécution publique vide()
{
essayer
{
enumerate(répertoire de démarrage);
file d'attente.put(DUMMY);
}
attraper (InterruptedException e)
{
}
}
/**
* Énumère récursivement tous les fichiers d'un répertoire donné et de ses sous-répertoires
* @param directory le répertoire dans lequel démarrer
*/
public void enumerate (répertoire de fichiers) lance une exception InterruptedException
{
Fichier[] fichiers = répertoire.listFiles();
pour (Fichier fichier : fichiers)
{
if (file.isDirectory()) enumerate(file);
sinon queue.put(fichier);
}
}
Fichier statique public DUMMY = new File("");
file d'attente privée BlockingQueue<File> ;
répertoire de démarrage de fichier privé ;
}
/**
* Cette tâche recherche les fichiers pour un mot-clé donné.
*/
la classe SearchTask implémente Runnable
{
/**
* Construit une SearchTask.
* @param file d'attente la file d'attente à partir de laquelle prendre les fichiers
* @param password le mot-clé à rechercher
*/
public SearchTask (file d'attente BlockingQueue<File>, mot-clé String)
{
this.queue = file d'attente ;
this.keyword = mot-clé ;
}
exécution publique vide()
{
essayer
{
booléen done = faux ;
pendant que (!fait)
{
Fichier fichier = file d'attente.take();
si (fichier == FileEnumerationTask.DUMMY)
{
file d'attente.put(fichier);
fait = vrai ;
}
sinon recherche (fichier);
}
}
attraper (IOException e)
{
e.printStackTrace();
}
attraper (InterruptedException e)
{
}
}
/**
* Recherche dans un fichier un mot-clé donné et imprime toutes les lignes correspondantes.
* @param file le fichier à rechercher
*/
la recherche publique vide (fichier fichier) lève IOException
{
Scanner dans = nouveau Scanner (nouveau FileInputStream (fichier));
int numéro de ligne = 0 ;
tandis que (dans.hasNextLine())
{
numéro de ligne++ ;
Ligne de chaîne = in.nextLine().trim();
if (line.contains(keyword)) System.out.printf("%s:%d %s%n", file.getPath(), lineNumber, line);
}
joindre();
}
file d'attente privée BlockingQueue<File> ;
mot-clé de chaîne privée ;
}