J'ai une situation difficile, futur.isdone () code> retourne
false code>, même si le thread est terminé.
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class DataAccessor {
private static ThreadPoolExecutor executor;
private int timeout = 100000;
static {
executor = new ThreadPoolExecutor(10, 10, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000));
}
public static void main(String[] args) {
List<String> requests = new ArrayList<String>();
for(int i=0; i<20; i++){
requests.add("request:"+i);
}
DataAccessor dataAccessor = new DataAccessor();
List<ProcessedResponse> results = dataAccessor.getDataFromService(requests);
for(ProcessedResponse response:results){
System.out.println("response"+response.toString()+"\n");
}
executor.shutdown();
}
public List<ProcessedResponse> getDataFromService(List<String> requests) {
final CountDownLatch latch = new CountDownLatch(requests.size());
List<SubmittedJob> submittedJobs = new ArrayList<SubmittedJob>(requests.size());
for (String request : requests) {
Future<ProcessedResponse> future = executor.submit(new GetAndProcessResponse(request, latch));
submittedJobs.add(new SubmittedJob(future, request));
}
try {
if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
// some of the jobs not done
System.out.println("some jobs not done");
}
} catch (InterruptedException e1) {
// take care, or cleanup
for (SubmittedJob job : submittedJobs) {
job.getFuture().cancel(true);
}
}
List<ProcessedResponse> results = new LinkedList<DataAccessor.ProcessedResponse>();
for (SubmittedJob job : submittedJobs) {
try {
// before doing a get you may check if it is done
if (!job.getFuture().isDone()) {
// cancel job and continue with others
job.getFuture().cancel(true);
continue;
}
ProcessedResponse response = job.getFuture().get();
results.add(response);
} catch (ExecutionException cause) {
// exceptions occurred during execution, in any
} catch (InterruptedException e) {
// take care
}
}
return results;
}
private class SubmittedJob {
final String request;
final Future<ProcessedResponse> future;
public Future<ProcessedResponse> getFuture() {
return future;
}
public String getRequest() {
return request;
}
SubmittedJob(final Future<ProcessedResponse> job, final String request) {
this.future = job;
this.request = request;
}
}
private class ProcessedResponse {
private final String request;
private final String response;
ProcessedResponse(final String request, final String response) {
this.request = request;
this.response = response;
}
public String getRequest() {
return request;
}
public String getResponse() {
return response;
}
public String toString(){
return "[request:"+request+","+"response:"+ response+"]";
}
}
private class GetAndProcessResponse implements Callable<ProcessedResponse> {
private final String request;
private final CountDownLatch countDownLatch;
GetAndProcessResponse(final String request, final CountDownLatch countDownLatch) {
this.request = request;
this.countDownLatch = countDownLatch;
}
public ProcessedResponse call() {
try {
return getAndProcessResponse(this.request);
} finally {
countDownLatch.countDown();
}
}
private ProcessedResponse getAndProcessResponse(final String request) {
// do the service call
// ........
if("request:16".equals(request)){
throw (new RuntimeException("runtime"));
}
return (new ProcessedResponse(request, "response.of." + request));
}
}
}
4 Réponses :
Le problème est probablement l'un des timings. Le loquet sera libéré avant em> toutes les tâches sont réellement complètes en ce qui concerne l'avenir (car le compte à rebours Vous recréez essentiellement le travail d'un Terminerservice (implémentation est ExecutorCompletservice a>), je recommanderais d'utiliser cela à la place. Vous pouvez utiliser la méthode () code> est dans l'appel
() code> méthode). P>
sondage (délai d'attente) code> pour obtenir les résultats. Il suffit de garder une trace du temps total et assurez-vous de réduire votre délai d'attente sur chaque appel à la durée totale restante. P>
Où dois-je appeler compte à rebours () alors?
Le compte à rebours est dans la bonne position, mais vous ne pouvez pas compter sur le compte à rebours pour être synchronisé avec l'avenir. Ils ont leurs propres contrôles de synchronisation distincts. C'est le comteTTownLatch peut avoir toutes les parties arrivées, mais le futuretk n'a pas encore été signalé son complète. Vous avez besoin d'un ou d'un autre.
@Johnvint donc, au lieu de vérifier futur.Isdone, je vais directement appeler future.get (manymtimeTout)? Ou y a-t-il un autre bon moyen de faire cela? Pouvez-vous expliquer dans une réponse séparée?
@YADAB Sûr je vais. Mais cette réponse répond à votre question au cas où vous prévoyez d'en accepter un.
Comme Jtahlborn a mentionné qu'il s'agit probablement d'une condition de race dans laquelle le compte-vient-fond signale ses fils d'attente, que les threads d'attente évaluent la condition d'annulation de l'avenir avant que le futuretask termine son exécution (qui se produira à un moment donné après le compte à rebours Vous ne pouvez tout simplement pas compter sur les mécanismes de synchronisation du compte-comteThlatch pour être synchronisé avec les mécanismes de synchronisation d'un avenir. Ce que vous devriez faire, c'est compter sur le futur pour vous dire quand c'est fait. p>
Vous pouvez < / code>). p>
futur.get (long timeout, timeunit.milliseconds) code> au lieu de
compte à rebours à compter à niveaux.millisecondes) code>. Pour obtenir le même type d'effet que le loquet, vous pouvez ajouter tous les
futur code> s à une liste code> code>, itérer sur la liste et sur chaque avenir. P>
Voici le scénario de la condition de course:
latch.await code>, il ne reçoit aucune machine à sous CPU du planificateur Java pour Millisecondes Li>
- Les derniers appels de threads exécutants
comptedidlatch.Countdown () code> dans le enfin code> clause li>
- Le planificateur Java décide de donner plus de priorité au fil principal car il a été aussi attendu pendant un moment li>
- En conséquence, lorsqu'il demande le dernier résultat code> futur code>, il n'est pas encore disponible car le dernier fil d'exécuteur n'a pas de tranches de temps pour propager le résultat, il est toujours dans
enfin code> ... li>
ul> Je n'ai pas trouvé d'explication détaillée sur la manière dont le planificateur Java fonctionne vraiment, probablement parce que cela dépend principalement du système d'exploitation exécutant la JVM, mais de manière générale, il essaie de donner également à la CPU aux fils endurlables en moyenne sur un période de temps. C'est pourquoi le thread principal peut atteindre le test code> iSdone code> avant que l'autre ait laissé le enfin code> clause. P> Je propose que vous modifiez vos résultats. latch.await code>. Comme vous le savez, le loquet a été diminué à zéro (sauf si le fil principal a été interrompu), tous les résultats doivent être disponibles vraiment bientôt. La méthode d'obtention avec le délai d'attente Laissez le planificateur la chance d'affecter une tranche de temps au dernier fil toujours en attente de la clause enfin: p> xxx pré> une remarque: forte > Votre code n'est pas réaliste que la méthode code> gettandProcessResponse code> se termine par moins d'un millisecondes. Avec un sommeil aléatoire là-bas, la condition de course ne sort pas si souvent. P> p>
I Deuxièmement les opinions sur les conditions de course.
Je suggérerais d'oublier le loquet et l'utilisation
java.util.concurrent.threadpoolEcutor.awaitTermination (long, timeunit) code> p>
Votre code n'a pas de sens.
PooledExecutor.submit (LCALLABLE) CODE> Ne fonctionne pas, où instanciez-vous le
appelable code>? Où créez-vous le loquet et où attendez-vous?
Mon soupçon est que les appelables peuvent lancer une exception non capturée, ce qui aborde par conséquent les threads du travailleur, cependant comptez également le loquet avant de quitter
appel code>.
@ Pétertörök qui ne vient pas sous exécutionException? Pouvez-vous s'il vous plaît expliquer?
Vous obtenez seulement une exception Execitionnée si vous appelez Future.get (). J'utiliserais
pour (futur Future: Futures) Future.get (); code> au lieu d'utiliser un décompte vers le bas ou une vérification de l'ISDONE ().
@YADAB, je veux dire que vous n'atteignez aucune exception dans
appel code>, ni définir aucun gestionnaire pour par exemple. exceptions non capturées. Donc, si par exemple. Une exception de pointeur nulle est lancée dans
appel code>, elle sera propagée vers le haut, abandonnant finalement le fil de travail sans rien remarquer.
YADAB Pouvez-vous nous montrer un cas de test de travail?
@ Pétertörök Merci de réponse, mon doute et Javadoc dit s'il y a une exception que l'avenir devrait être mis à faire? Ou n'est-il pas mis à faire s'il existe une exception d'exécution?
@Johnvint pouvez-vous s'il vous plaît expliquer "cas de test de travail"?
Pouvez-vous écrire un cas de test avec une méthode simple
principale () code> que si nous devions exécuter, nous verrions ce que vous voyez.
@YADAB, j'ai aussi vérifié le Javadoc depuis et cela suggère en effet que ma suspicion est fausse. Je recommande toujours de faire face aux exceptions d'une manière ou d'une autre si vous utilisez des threads.
@Johnvint a fourni un exemple détaillé.
Ce bogue se produit-il à chaque fois ou seulement parfois?