2009-08-04 7 views
2

J'ai un sémaphore qui limite les utilisateurs à télécharger n nombre de fichiers à la fois. Chaque fichier est téléchargé dans un fil séparé.Problème dans le sémaphore étant interrompu plusieurs fois

EDIT: l'exemple modifié de sorte que la libération se produise correctement

import java.util.concurrent.Semaphore; 
public void downloadFile() { 
    Thread downloadThread = new Thread() { 
     boolean bSemaphoreAcquired = false; 
     public void run() { 
      try { 
       semaphore.acquire(); 
       bSemaphoreAcquired = true; 
       // do the download 
      } catch (InterruptedException e) { 
      } finally { 
       if (bSemaphoreAcquired) semaphore.release(); 
      } 
     } 
    }; 
    // add download thread to the list of download threads 
    downloadThread.start(); 
} 

Maintenant, tous les nouveaux téléchargements attendront les téléchargements précédents pour terminer une fois que tous les permis de sémaphores ont été acquis.

Lorsque l'utilisateur choisit d'annuler un téléchargement en attente à l'appel d'acquisition, j'appelle la méthode interruption() pour terminer ce fil de téléchargement. Le problème auquel je suis confronté est qu'une fois que ce sémaphore a été interrompu, il ne jetterait pas l'exception InterruptedException la deuxième fois !! Tout nouveau thread créé attendra la méthode acquérir pour toujours!

Sequence of events (Semaphore that permits 2 threads) 
- thread 1 downloading 
- thread 2 downloading 
- thread 3 waiting on acquire() 
- thread 3 is cancelled (interrupt() is called). InterruptedException is thrown and the thread exits 
- thread 4 is created and is now waiting on acquire() 
- thread 4 is cancelled (interrupt() is called). InterruptedException IS NOT THROWN ANYMORE!!! 

Ceci est la trace de la pile de fil 4

Semaphore$FairSync(AbstractQueuedSynchronizer).fullGetFirstQueuedThread() line: 1276  
Semaphore$FairSync(AbstractQueuedSynchronizer).getFirstQueuedThread() line: 1232  
Semaphore$FairSync.tryAcquireShared(int) line: 201 
Semaphore$FairSync(AbstractQueuedSynchronizer).acquireSharedInterruptibly(int) line: 1142 
Semaphore.acquire() line: 267 
FileDownloadManager$1.run() line: 150 

Pourquoi fil 4 reçoit pas l'exception?

+1

Cela ne contribue pas à votre problème, mais vous devez mettre la « libération » appel en bloc finally, afin qu'il n'y ait pas de fuite de sémaphores si devrait y avoir un exception lors du téléchargement. – Thilo

+0

Que se passe-t-il si vous l'interrompez une troisième fois? Êtes-vous sûr qu'il attend sur le "acquérir"? Pouvez-vous obtenir un vidage de thread JVM (kill -QUIT) pour vous en assurer? – Thilo

+0

Tous les nouveaux threads attendent juste d'acquérir et ne peuvent pas être interrompus une fois qu'il a atteint cet état. Maintenant, même si le thread d'origine 1 et le thread 2 terminent l'exécution et libèrent le sémaphore, les nouveaux threads continuent à attendre indéfiniment sur acquire() indéfiniment !! Ajout de la trace de la pile à la question. – Prashast

Répondre

2

Je suggère d'utiliser un pool de threads standard au lieu de sémaphores. Le problème avec votre solution est que vous créez un nouveau thread indépendamment du fait que vous ayez atteint la limite maximale ou non. Donc, si vous avez 1000 demandes simultanées, vous allez créer 1000 threads, ce qui est vraiment inutile.

Essayez plutôt quelque chose comme ceci:

ExecutorService executorService = Executors.newFixedThreadPool(5); 
    executorService.submit(new Runnable() { 
     public void run() { 
      // do download 
     } 
    }); 
+1

Merci. Cela a réglé mon problème. Je suis toujours curieux de savoir ce qui se passait avec ma solution de sémaphore. – Prashast

+0

@Prashast: est-il possible que vous interrompiez un thread différent? –

+0

Non, je suis tout à fait certain d'interrompre le fil correct. Vérifié plusieurs fois en parcourant le code. – Prashast

0

Libérez-vous le sémaphore dans la capture? Pourquoi ne pas mettre le try-catch dans l'aquire-release? Je ne sais pas ce que java fait, mais ce ne serait pas plus logique. De cette façon, tout problème au sein de l'essai ... catch se termine toujours par la libération du sémaphore.

+1

J'ai essayé de relâcher le sémaphore mais cela n'aide pas, il a toujours le même comportement. En outre, aucune autre exception n'est lancée dans cette fonction. Le seul possible est l'exception InterruptedException lancée par acquire() (et je ne pense pas que je devrais libérer le sémaphore quand le thread ne l'a jamais acquis en premier lieu). – Prashast

+1

@Prashast: Oui, vous avez raison. Vous devez vous assurer que vous ne "libérez" jamais un sémaphore que vous n'avez pas obtenu (sauf si vous implémentez une sorte de logique plutôt bizarre). – Thilo

Questions connexes