7
votes

Java ThreadPool Usage

J'essaie d'écrire un robot Web multithreadé.

Ma classe d'entrée principale a le code suivant: xxx

L'URLCRAWLER récupère l'URL spécifiée, analyse la HTML extrait des liens de celui-ci et planifie les liens invisibles à la frontière.

Une frontière est une file d'attente d'URL non attribuées. Le problème est de savoir comment écrire la méthode get (). Si la file d'attente est vide, il devrait attendre que les urlrofrawlers finissent puis réessayer. Il devrait renvoyer NULL uniquement lorsque la file d'attente est vide et il n'y a pas d'urlcroudler actuellement actif.

Ma première idée était d'utiliser un atomicinteger pour compter le nombre actuel d'urlrofrastres de travail et un objet auxiliaire pour notifier () / attendre () appels. Chaque robot sur le démarrage incrémente le nombre d'urlrofrawlers de travail actuels, et à la sortie décrémente l'objet, et informer l'objet qu'il a terminé.

mais je lis cela notify () et wait () et wait () Des méthodes quelque peu obsolètes pour faire la communication de fil.

Que dois-je utiliser dans ce modèle de travail? Il est similaire à M. producteurs et consommateurs de N, la question est de savoir comment faire face à l'exaystion des producteurs.


0 commentaires

6 Réponses :


3
votes

Je ne suis pas sûr de comprendre votre conception, mais cela peut être un travail pour un Semaphore


0 commentaires

3
votes

Une option consiste à faire une «frontière» une file d'attente de blocage, de sorte que tout fil essayant de «obtenir» de celui-ci bloquera. Dès que tout autre URLCRAWLER met des objets dans cette file d'attente, tous les autres threads seront automatiquement notifiés (avec l'objet dégueulé)


4 commentaires

Oui, c'est une solution pour un état stable. Mais comment traiter alors avec la situation quand aucun des urlrowlers files d'une URL? Avec une file d'attente de blocage, la frontière bloquera infiniment.


Dans ce cas, vous pouvez avoir une méthode de crawlerdone () sur votre objet frontier appelé à chaque fois qu'un Urlcrawler finit de fonctionner. Cette méthode avec l'approche de comptoir que vous avez suggérée, vous pouvez tester (dans votre méthode frontière) si tous les crawlers ont fini. Si cela est vrai, le get () peut retourner null sans bloquer


La frontière peut être une file d'attente de blocage de capacité fixe. Un bon candidat à cette capacité est le nombre de trajets


@Loop: Si les robots d'essai en font plus d'une URL (ce qui semble probable), il y aura une impasse. Si vous utilisez une file d'attente de blocage sans bornes, vous devrez utiliser des "messages spéciaux" pour prier le fil de blocage de la file d'attente lorsque vous détectez que l'activité est terminée (quel problème, vous devez toujours résoudre encore). Donc, tout dans tout ce que je ne pense pas qu'une file d'attente de blocage aidera ici ..



2
votes

Je pense que l'utilisation d'attente / notification est justifiée dans ce cas. Je ne peux pas penser à un moyen simple de le faire en utilisant j.u.c.
Dans une classe, appelons coordinatrice: xxx

puis, xxx


0 commentaires

2
votes

Je pense qu'un bloc de construction de base pour votre cas d'utilisation est un «loquet», similaire à celui de CountownLatch, mais contrairement à CountownLatch, une qui permet incrémentation le compte également.

une interface pour telle Un loquet peut être xxx

valeurs juridiques pour les comptes serait de 0 et plus. La méthode attendre () vous permettrait de bloquer jusqu'à ce que le nombre diminue à zéro.

Si vous avez un tel loquet, votre cas d'utilisation peut être décrit assez facilement. Je soupçonne aussi que la file d'attente (frontière) peut être éliminée dans cette solution (Executor en fournit un de tout de temps afin qu'il soit quelque peu redondant). Je réécrireais votre routine principale comme xxx

Votre URLCRAWLER utiliserait le loquet de cette manière: xxx

comme pour le Des implémentations de verrouillage, il peut y avoir un certain nombre d'implémentations possibles, allant de celui qui est basé sur WAIT () et notifyall (), un, qui utilise un verrouillage et une condition, à une implémentation qui utilise l'abstractoStueUserSynchroniseur. Toutes ces implémentations que je pense seraient assez simples. Notez que l'attente () - notifier () la version et la version de verrouillage seraient basées sur l'exclusion mutuelle, alors que la version AQS utiliserait CAS (comparer-et--Swap) et pourrait donc améliorer mieux dans certaines situations.


2 commentaires

Votre loquet personnalisé ressemble beaucoup à un sémaphore ... Pourquoi ne pas en utiliser un?


Oui, il y a des similitudes à coup sûr. Une chose qui manque au sémaphore de la vanille est la méthode attendre () au-dessus de laquelle le terme sémaphore peut bloquer jusqu'à ce que tous les permis soient libérés. On peut probablement créer ceci en combinant un sémaphore et un loquet de compte à rebours.



0
votes

J'aimerais suggérer un adaptatifExecuter. Sur la base d'une valeur caractéristique, vous pouvez choisir de sérialiser ou de paralliser un fil à exécution. Dans l'échantillon ci-dessous, Puid est une chaîne / objet que je voulais utiliser pour faire cette décision. Vous pouvez modifier la logique en fonction de votre code. Certaines portions de code sont commentées pour permettre d'autres expériences.

CLASSE AdaptiveExecuteur implémente exécuteur { Tâches finales de la file d'attente = nouveau LinkedBlockingQueue (); Actif exécutable; // exécutantService filexecutor = exécuteurs.NewcachedTheadpool (); Static ExecuTertorservice Threadexecutor = exécuteurs.NewfixedTheadpool (4); xxx

}


0 commentaires

2
votes

La question est un peu ancienne, mais je pense avoir trouvé une solution de travail simple,

étendre la classe ThreadPoolEcutor comme ci-dessous. La nouvelle fonctionnalité conserve le nombre de tâches actif (malheureusement, à condition que gettactivecount () n'est pas fiable). Si taskcount.get () == 0 et il n'y a plus de tâches en file d'attente, cela signifie qu'il n'y a rien à faire et que l'exécuteur s'arrête. Vous avez vos critères de sortie. De plus, si vous créez votre exécuteur, mais ne soumettez aucune tâche, il ne bloquera pas: xxx

Une autre chose que vous devez faire est de mettre en œuvre votre exécutable d'une manière qu'il conserve référence à exécuteur que vous utilisez pour pouvoir soumettre de nouvelles tâches. Voici une simulation: xxx


0 commentaires