J'apprends une concurrence dans Java et je suis allé sur les didacticiels sur le site Web Oracle. Bien que j'en ai compris une partie, une plus grande partie m'éluve. Je pensais à un problème hypothétique (bien que cela puisse ou peut ne pas être un bon cas d'utilisation de plusieurs threads) où j'ai 100 fichiers texte et j'ai besoin de rechercher un mot particulier dans tous. Si je mettez en place une file d'attente de blocage, et je ne veux pas utiliser un ThreadPool
avec un service exécuteur:
BlockingQueue
avec plusieurs producteurs, plusieurs producteurs de consommateurs à l'esprit dans lesquels j'ai eu 100 threads à Met () 100 Contenu du fichier texte dans le BlockingQueue
et un autre 100 à -prise () et recherchez un mot particulier en eux?Ce que j'ai écrit mai ou peut ne pas avoir de sens, mais je ne suis qu'un débutant et que je veux en savoir plus à ce sujet car ce problème est souvent apparu dans les entretiens de programmation.
Grande question! J'ai écrit un petit exemple (il utilise uniquement 6 threads, mais peut facilement être développé) pour illustrer la manière dont vous pouvez lire plusieurs fichiers (un thread pour lire chaque fichier) et traiter les données avec plusieurs threads.
Alors commençons par le Controller
qui est essentiellement juste le directeur chargé de la création et de la gestion des autres threads. Vous remarquerez qu'il donne à chaque fil une référence à la file d'attente qui permet aux threads de faire leur travail - en ajoutant des articles ou en supprimant les éléments de la file d'attente. Vous remarquerez également qu'il conserve deux collections de threads - une pour les threads du producteur et un autre pour tous les fils. La collection de threads du producteur est utilisée pour fournir un moyen de savoir si elles devraient continuer à attendre plus d'intrants. La collection tenant tous les threads est utilisée pour empêcher le contrôleur de sortir avant que tous les producteurs et les consommateurs aient terminé leur travail.
package multithreading.producer_consumer.blockingQueue;
import Java.nio.file.Paths;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.concurrent.BlockingQueue;
import Java.util.concurrent.LinkedBlockingDeque;
public class Controller {
private static final int NUMBER_OF_CONSUMERS = 3;
private static final int NUMBER_OF_PRODUCERS = 3;
private static final int QUEUE_SIZE = 2;
private static BlockingQueue<String> queue;
private static Collection<Thread> producerThreadCollection, allThreadCollection;
public static void main(String[] args) {
producerThreadCollection = new ArrayList<Thread>();
allThreadCollection = new ArrayList<Thread>();
queue = new LinkedBlockingDeque<String>(QUEUE_SIZE);
createAndStartProducers();
createAndStartConsumers();
for(Thread t: allThreadCollection){
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Controller finished");
}
private static void createAndStartProducers(){
for(int i = 1; i <= NUMBER_OF_PRODUCERS; i++){
Producer producer = new Producer(Paths.get("./src/multithreading/producer_consumer/blockingQueue/file"+i+".txt"), queue);
Thread producerThread = new Thread(producer,"producer-"+i);
producerThreadCollection.add(producerThread);
producerThread.start();
}
allThreadCollection.addAll(producerThreadCollection);
}
private static void createAndStartConsumers(){
for(int i = 0; i < NUMBER_OF_CONSUMERS; i++){
Thread consumerThread = new Thread(new Consumer(queue), "consumer-"+i);
allThreadCollection.add(consumerThread);
consumerThread.start();
}
}
public static boolean isProducerAlive(){
for(Thread t: producerThreadCollection){
if(t.isAlive())
return true;
}
return false;
}
}
Ensuite, voici le code de la classe Producer
qui sera utilisé pour créer tous les threads dont le travail est de lire un seul fichier chacun. Vous verrez que le producteur lit une ligne de fichier spécifique par ligne et ajoute ces lignes à la file d'attente car il existe un espace disponible en utilisant la méthode put
.
package multithreading.producer_consumer.blockingQueue;
import Java.io.BufferedReader;
import Java.io.IOException;
import Java.nio.file.Files;
import Java.nio.file.Path;
import Java.util.concurrent.BlockingQueue;
public class Producer implements Runnable{
private Path fileToRead;
private BlockingQueue<String> queue;
public Producer(Path filePath, BlockingQueue<String> q){
fileToRead = filePath;
queue = q;
}
@Override
public void run() {
try {
BufferedReader reader = Files.newBufferedReader(fileToRead);
String line;
while((line = reader.readLine()) != null){
try {
queue.put(line);
System.out.println(Thread.currentThread().getName() + " added \"" + line + "\" to queue, queue size: " + queue.size());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" finished");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Enfin, voici la classe Consumer
qui sera responsable de la lecture des données de la file d'attente et de le traiter de manière appropriée. Notez que cette classe n'utilise pas la méthode take
. Je l'ai écrit de cette façon pour que le programme se termine après le traitement de tous les fichiers. Si vous voulez que les consommateurs restent en vie, vous pouvez remplacer poll
avec take
(ainsi que quelques autres ajustements mineurs à la méthode run
comme la remise du InterruptedException
Cela pourrait survenir en attendant take
pour retourner une valeur).
package multithreading.producer_consumer.blockingQueue;
import Java.util.concurrent.BlockingQueue;
public class Consumer implements Runnable{
private BlockingQueue<String> queue;
public Consumer(BlockingQueue<String> q){
queue = q;
}
public void run(){
while(true){
String line = queue.poll();
if(line == null && !Controller.isProducerAlive())
return;
if(line != null){
System.out.println(Thread.currentThread().getName()+" processing line: "+line);
//Do something with the line here like see if it contains a string
}
}
}
}
Voici les 3 fichiers d'entrée que j'ai utilisés:
file1.txt
file #1 line 1
file #1 line 2
file #1 line 3
file #1 line 4
file #1 line 5
file2.txt
This is file #2 line 1
This is file #2 line 2
This is file #2 line 3
This is file #2 line 4
This is file #2 line 5
file3.txt
Lastly we have file #3 line 1
Lastly we have file #3 line 2
Lastly we have file #3 line 3
Lastly we have file #3 line 4
Lastly we have file #3 line 5
Voici quelques échantillons de sortie du programme. Notez que System.Out.println n'est pas synchronisé, la sortie n'est donc pas dans l'ordre.
consumer-0 processing line: Lastly we have file #3 line 1
consumer-0 processing line: This is file #2 line 1
producer-2 added "This is file #2 line 1" to queue, queue size: 1
producer-2 added "This is file #2 line 2" to queue, queue size: 1
producer-2 added "This is file #2 line 3" to queue, queue size: 1
producer-2 added "This is file #2 line 4" to queue, queue size: 2
consumer-1 processing line: file #1 line 1
consumer-1 processing line: This is file #2 line 4
consumer-1 processing line: This is file #2 line 5
producer-1 added "file #1 line 1" to queue, queue size: 1
producer-1 added "file #1 line 2" to queue, queue size: 0
producer-3 added "Lastly we have file #3 line 1" to queue, queue size: 0
producer-1 added "file #1 line 3" to queue, queue size: 1
consumer-1 processing line: file #1 line 2
producer-2 added "This is file #2 line 5" to queue, queue size: 0
producer-1 added "file #1 line 4" to queue, queue size: 2
producer-2 finished
consumer-2 processing line: This is file #2 line 3
consumer-2 processing line: Lastly we have file #3 line 2
consumer-2 processing line: file #1 line 4
consumer-2 processing line: file #1 line 5
consumer-0 processing line: This is file #2 line 2
producer-1 added "file #1 line 5" to queue, queue size: 0
producer-1 finished
consumer-1 processing line: file #1 line 3
producer-3 added "Lastly we have file #3 line 2" to queue, queue size: 2
producer-3 added "Lastly we have file #3 line 3" to queue, queue size: 1
producer-3 added "Lastly we have file #3 line 4" to queue, queue size: 1
producer-3 added "Lastly we have file #3 line 5" to queue, queue size: 0
producer-3 finished
consumer-0 processing line: Lastly we have file #3 line 3
consumer-2 processing line: Lastly we have file #3 line 5
consumer-1 processing line: Lastly we have file #3 line 4
Controller finished
J'espère que cela vous aidera à illustrer la manière dont vous pourriez accomplir votre tâche sans utiliser ExecuTtorservice. S'amuser!