6
votes

Comment dois-je concevoir ce programme?

J'écris un programme en Java. L'image est explicite -

 Conception La méthode principale génère trois threads. Le processeur SAX traite le fichier XML d'entrée, génère des objets JAXB et les place dans le cache goyave. Le cache Guava est géré par un autre thread. Chaque fois qu'un objet entre dans le cache, ce thread notifie le troisième thread qui est le générateur MDL (il met en relation les objets JAXB similaires, les interconnecte et génère un autre fichier XML, appelé MDL). J'ai codé ce qui suit pour la classe principale -

package test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainClass {

    public static void main(String args[]) {

        ExecutorService xMLService = Executors.newFixedThreadPool(1);
        xMLService.execute(new XMLProcessor());

        ExecutorService cacheService = Executors.newFixedThreadPool(1);
        cacheService.execute(new CacheProcessor());

        ExecutorService mdlService = Executors.newFixedThreadPool(1);
        mdlService.execute(new MDLProcessor());

        xMLService.shutdown();
        cacheService.shutdown();
        mDLService.shutdown();
    }
}

Mais maintenant, j'ai des doutes sur la façon de passer des objets entre les threads et sur la façon de notifier le générateur MDL chaque fois qu'un nouvel objet arrive dans le cache. Dans l'ancien modèle de thread de Java, nous pourrions utiliser notify (), mais je veux utiliser le ExecutorService actuel. Et il existe des rappels asynchrones. Je me demande donc comment concevoir ce cadre. Comment passer des objets et notifier les threads? Nous conservons les objets de cache dans HashMap et le thread CacheService doit passer la clé au MDLService. Alors, quel modèle dois-je utiliser?


4 commentaires

Bonjour. C'est un bon problème de conception. Je ne pense pas qu'il y ait une solution absolue. OMI, c'est un bon cas d'utilisation pour introduire le traitement des files d'attente. Votre XMLProcessor publie les résultats dans une file d'attente puis votre CacheProcessor écoute la file d'attente et traite les éléments et même pour CacheService et MDLService .


Dans votre programme, vous n'avez pas besoin de trois threads pour fonctionner, le thread utilisé pour traiter peut être supprimé, puis le Guava Cache n'a pas besoin non plus, vous pouvez utiliser un BlockingQueue pour traiter l'entrée XML du processeur AX et l'ajouter à BlockingQueue et MDL générateur obtenir des données de BlockingQueue.si vous avez besoin d'un exemple de code, je peux prendre en charge.


Salut @TongChen: J'ai besoin du cache Guava car le générateur MDL doit l'interroger plusieurs fois avec le hashkey (qui doit être passé à partir du thread Cache lui-même) pour générer le MDL. Donc, si les objets sont dans le cache, ce sera plus rapide. En fait, nous pouvons mettre le xMLService et le cacheService dans le même thread si cela simplifie le problème. Ainsi, le premier thread génère un hashmap, notifie le second thread et passe le hashkey. Comment cela peut-il être fait? La méthode run () n'accepte pas les arguments. Alors, comment pouvons-nous notifier et transmettre une chaîne en même temps?


Vous voudrez peut-être jeter un œil à Guava EventBus . C'est un peu primitif dans ce qu'il peut faire (et toujours bêta), mais utilisable pour des cas simples. Tous les exécuteurs se déplaceraient sous le contrôle EventBus . Passer un objet se transforme en le postant sur le bus par le producteur et en invocation asynchrone du callback côté consommateur. Toute distribution est effectuée en fonction du type de classe de l'objet.


3 Réponses :


3
votes

Puisque vous utilisez Guava Cache, vous pouvez utiliser Guava AsyncEventBus pour répartir les messages entre les tâches et supprimer les trois services ExecutorServices séparés.


9 commentaires

Cela devrait être un commentaire plutôt qu'une réponse


@Stultuske Vous devez expliquer pourquoi. Ce n'était ni une demande de plus d'informations ni une suggestion d'amélioration mais une réponse à la question "comment passer des objets entre les threads et comment notifier le générateur MDL chaque fois qu'un nouvel objet arrive dans le cache". Le fait que la réponse soit courte ne signifie pas qu'elle devrait être un commentaire.


c'était en fait une question que vous lui posiez.


Votre demande est donc que je reformule ma réponse sous une forme qui ne contient pas de question? Bien sûr, continuez et modifiez le format de ma réponse en fonction de vos besoins.


ce ne sont pas mes besoins. Vous postez simplement quelque chose dont vous n'êtes pas sûr, vous vous indiquez qu'en gros, vous ne faites que deviner. c'est pourquoi c'est plus un commentaire


La question initiale est si large et à un niveau si élevé qu'il n'y a pas de réponse définitive. Mais cela n'a pas d'importance. Ma question ne portait pas sur la question elle-même. C'était une suggestion de solution, qui peut être considérée comme une réponse. Vous vous plaignez uniquement de la sémantique. "Vous pourriez probablement utiliser Guava AsyncEventBus" est la même réponse.


continuons cette discussion dans le chat .


ce n'est pas vraiment une discussion :) Je viens de donner mon avis, et, après avoir demandé ma raison, je l'ai donné aussi


J'ai édité un mot et comme par magie, tout est génial. On cool?



4
votes

Comment passer des objets et notifier les threads? Nous conservons les objets de cache dans HashMap et le thread CacheService doit passer la clé au MDLService. Alors, quel modèle dois-je utiliser?

Il me semble que vous avez 1 fil de trop. Le thread de lecture XML et l'écriture MDL ont du sens, mais un thread pour simplement mettre des choses dans un cache en mémoire semble être trop compliqué. Si le générateur MDL doit utiliser le cache Guava, il doit "posséder" le cache et y insérer des éléments.

Cela vous laisse 1 thread de processeur SAX d'entrée et 1 thread de générateur MDL de sortie. Bien. Pour connecter les deux, j'utiliserais un BlockingQueue comme le LinkedBlockingQueue . Vous pouvez ou non vouloir définir une limite de taille sur la file d'attente selon que la lecture est ou non plus rapide que l'écriture et du nombre d'enregistrements dans votre travail.

Ainsi, votre thread principal créera le BlockingQueue et le passera ensuite aux threads d'entrée et de sortie. Le thread d'entrée SAX appelle put () dans la file d'attente et le thread de sortie MDL appelle take () place l'objet dans le cache Guava puis effectue la génération MDL.

J'espère que cela vous aidera.


0 commentaires

2
votes

Voici un exemple d'implémentation pour le cas décrit ci-dessus. Veuillez noter que l'implémentation aurait pu être possible même sans le cache Guava, comme mentionné par certains des autres qui ont répondu; néanmoins je présume qu'il y avait peut-être une raison valable pour Nirmalaya de le demander. Une de ces raisons à laquelle je pourrais penser est le débordement du cache sur des périphériques de stockage ou des bases de données, pour économiser sur la mémoire d'exécution.

employee-records.xml

package com.technoroy.examples.guava;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

/**
 * The primary executable class
 * 
 * @author Rahul R
 *
 */
public class GuavaCacheProcessor {
    private final static BlockingQueue<Integer> notificationQueue = new LinkedBlockingQueue<>();

    public static void main(String... arguments) {
        Runnable xmlProcessor = new Runnable() {
            public void run() {
                parseDataFile();
            }
        };

        Runnable mdlGenerator = new Runnable() {
            public void run() {
                try {
                    while (true) {
                        Integer id = notificationQueue.take();
                        Employee record = ApplicationCacheUtil.getRecord(id);
                        generateContent(record);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        };

        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(xmlProcessor);
        executorService.submit(mdlGenerator);
    }

    public static void generateContent(Employee employee) {
        System.out.println(employee);
    }

    public static void parseDataFile() {
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        InputStream dataInputStream = GuavaCacheProcessor.class.getResourceAsStream("employee-records.xml");

        try {
            SAXParser saxParser = saxParserFactory.newSAXParser();
            saxParser.parse(dataInputStream, new DefaultHandler() {
                private Employee employee = null;
                private StringBuilder elementValue = null;

                @Override
                public void startElement(String uri, String localName, String qName, Attributes attributes)
                        throws SAXException {
                    if (qName.equalsIgnoreCase("Employee")) {
                        employee = new Employee();

                        String id = attributes.getValue("id");
                        if (id.matches("-?\\d+(\\.\\d+)?")) {
                            employee.setId(Integer.valueOf(id));
                        }
                    }

                    elementValue = new StringBuilder();
                }

                @Override
                public void characters(char ch[], int start, int length) throws SAXException {
                    if (elementValue != null) {
                        elementValue.append(new String(ch, start, length));
                    }
                }

                @Override
                public void endElement(String uri, String localName, String qName) throws SAXException {
                    if (qName.equalsIgnoreCase("name")) {
                        if (employee != null && elementValue != null) {
                            employee.setName(elementValue.toString());
                        }
                    } else if (qName.equalsIgnoreCase("Employee")) {
                        ApplicationCacheUtil.putRecord(employee.getId(), employee);
                        try {
                            notificationQueue.put(employee.getId());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    elementValue = null;
                }
            });
        } catch (ParserConfigurationException | SAXException | IOException e) {
            e.printStackTrace();
        }
    }
}

/**
 * The Cache utilities class, that initializes and returns a handle to the
 * cache.
 * 
 * @author Rahul R
 *
 */
class ApplicationCacheUtil {
    private static Cache<Integer, Employee> cache = CacheBuilder.newBuilder().build();

    public static Cache<Integer, Employee> getCache() {
        return cache;
    }

    public static void putRecord(Integer key, Employee value) {
        cache.put(key, value);
    }

    public static Employee getRecord(Integer key) {
        return cache.getIfPresent(key);
    }
}

package com.technoroy.examples.guava;

/**
 * A value holder POJO implementation for Employee records
 * @author Rahul R
 *
 */
class Employee {
    private Integer id = null;
    private String name = null;

    public Employee() {
        super();
    }

    public Employee(Integer id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + "]";
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<Employees>
    <Employee id="1">
        <name>Thomas</name>
    </Employee>
    <Employee id="2">
        <name>Lisa</name>
    </Employee>
    <Employee id="3">
        <name>Ronald</name>
    </Employee>
    <Employee id="4">
        <name>Erica</name>
    </Employee>
</Employees>


2 commentaires

Désolé, j'avais manqué le commentaire décrivant la raison pour laquelle @Nirmalaya doit utiliser le cache Guava, dans l'un de ses commentaires à TongChen.


Salut @Rahul R - merci beaucoup pour la réponse la plus détaillée à laquelle j'aurais pu m'attendre! Inclinez-vous.