2011-06-20 3 views
1

J'ai une boucle principale qui envoie des requêtes à un système externe. Le système externe peut prendre quelques secondes ou même quelques minutes pour répondre.
En outre, si le nombre de requêtes atteint MAX_REQUESTS, la boucle for actuelle doit être SLEEP pendant quelques secondes.Java Thread Sleep

Ceci est mon scénario. Disons que la boucle for principale se met en veille pendant 5 secondes parce qu'elle a atteint le MAX_REQUESTS. Ensuite, dites qu'une requête externe précédente renvoie des retours de callExternalSystem(). Qu'adviendra-t-il du thread principal de boucle fortifiée qui est actuellement dans l'état SLEEP? Sera-t-il interrompu et poursuivra le traitement ou continuera-t-il de dormir?

for(...){ 
    ... 
    while(numRequestsProcessing > MAX_REQUESTS){ 
    Thread.sleep(SLEEP_TIME); 
    } 
    ... 
callExternalSystem(); 

} 

Merci d'avance.

+0

ajoutez-vous un sommeil dans une tentative d'autoriser numRequestsProcessing à descendre? donc vous ne permutez pas le service? un pool de threads fixe vous donnerait (plus ou moins) ou faire une co-op inter-thread via wait/notifiy/conditions ..? – Toby

Répondre

5

À moins d'avoir du code pour interrompre le thread endormi, il continuera à dormir jusqu'à ce que le temps requis se soit écoulé. Si vous ne voulez pas que cela se produise, vous pouvez éventuellement utiliser wait()/notify() au lieu de sleep() afin qu'un autre thread puisse avertir l'objet sur lequel le thread principal dort, afin de le réveiller. Cela dépend de étant un autre thread à remarquer que le système externe a répondu, bien sûr - ce n'est pas vraiment clair comment vous obtenez des réponses.

EDIT: Il semble que vous devriez vraiment utiliser un Semaphore. Chaque fois que le thread principal souhaite émettre une requête, il acquiert un permis. Chaque fois qu'il y a une réponse, cela libère un permis. Ensuite, vous avez juste besoin de le configurer avec autant de permis que vous le souhaitez. Utilisez tryAcquire si vous souhaitez être en mesure de spécifier un délai d'attente dans le thread principal - mais pensez à ce que vous voulez faire si vous avez déjà autant de demandes en attente que vous êtes vraiment satisfait.

+1

+1 bien que je voudrais aller plus loin, et dire que l'utilisation du sommeil pour la planification ici est un anti-modèle. Sûrement il y a un événement qui peut être accroché pour les réponses entrantes, pour rendre le réveil déterministe? –

+0

Ok merci. callExternalSystem() crée en fait un autre Thread pour appeler le système externe. Quand il reçoit une réponse, il décrémente numRequestsProcessing de 1. C'est ainsi que le thread principal de la boucle principale saura quand ne pas se remettre en veille. – Marquinio

+0

@Marquinio: Droit. Ce fil * pourrait * informer l'appelant ... mais il y a une meilleure alternative. Modification –

3

J'utiliserais java.util.concurrent.Executors pour créer un pool de threads avec des threads MAX_REQUESTS. Créez un fichier java.util.concurrent.CountDownLatch pour les nombreuses requêtes que vous envoyez en même temps. Passez le verrou aux Runnables qui effectuent la requête, ils appellent countDown() sur le verrou une fois l'opération terminée. Le thread principal appelle ensuite await (timeout) sur le verrou. Je suggère également le livre "Java Concurrency in Practice".

1

Une approche consiste à utiliser un ThreadPoolExecutor qui bloque chaque fois qu'il n'y a pas de thread libre.

ThreadPoolExecutor executor = new ThreadPoolExecutor(MAX_REQUESTS, MAX_REQUESTS, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new RejectedExecutionHandler() { 
    @Override 
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 
     try { 
      executor.getQueue().offer(r, Long.MAX_VALUE, TimeUnit.NANOSECONDS); 
     } catch (InterruptedException e) { 
      Thread.currentThread().interrupt(); 
     } 
    } 
}); 
for(int i=0;i<LOTS_OF_REQUESTS;i++) { 
    final int finalI = i; 
    executor.submit(new Runnable() { 
     @Override 
     public void run() { 
      request(finalI); 
     } 
    }); 
} 

Une autre approche consiste à demander aux tâches de générer leurs propres demandes. De cette façon, une nouvelle requête est générée chaque fois qu'un thread est libre simultanément.

ExecutorService executor = Executors.newFixedThreadPool(MAX_REQUESTS); 
final AtomicInteger counter = new AtomicInteger(); 
for (int i = 0; i < MAX_REQUESTS; i++) { 
    executor.submit(new Runnable() { 
     @Override 
     public void run() { 
      int i; 
      while ((i = counter.getAndIncrement()) < LOTS_OF_REQUESTS) 
       request(i); 
     } 
    }); 
}