2009-08-15 6 views
7

Dans le code ci-dessous, j'attrape une exception TimeoutException après 100 secondes comme prévu. À ce stade, je m'attendrais à ce que le code sorte de main et que le programme se termine, mais il continue d'imprimer sur la console. Comment puis-je arrêter la tâche après l'expiration du délai?Comment obtenir FutureTask pour retourner après TimeoutException?

private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool(); 

private static <T> T timedCall(Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException { 
    FutureTask<T> task = new FutureTask<T>(c); 
    THREAD_POOL.execute(task); 
    return task.get(timeout, timeUnit); 
} 


public static void main(String[] args) { 

    try { 
     int returnCode = timedCall(new Callable<Integer>() { 
      public Integer call() throws Exception { 
       for (int i=0; i < 1000000; i++) { 
        System.out.println(new java.util.Date()); 
        Thread.sleep(1000); 
       } 
       return 0; 
      } 
     }, 100, TimeUnit.SECONDS); 
    } catch (Exception e) { 
     e.printStackTrace(); 
     return; 
    } 


} 

Répondre

8

Vous devez annuler votre tâche délai d'attente (et d'interrompre son fil). C'est ce que fait la méthode cancel(true). :

private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool(); 

private static <T> T timedCall(FutureTask<T> task, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException { 
    THREAD_POOL.execute(task); 
    return task.get(timeout, timeUnit); 
} 


public static void main(String[] args) { 
     try { 
      FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() { 
       public Integer call() throws Exception { 
         for (int i=0; i < 1000000; i++) { 
           if (Thread.interrupted()) return 1; 
           System.out.println(new java.util.Date()); 
           Thread.sleep(1000); 
         } 
         return 0; 
       } 
      }); 
      int returnCode = timedCall(task, 100, TimeUnit.SECONDS); 
     } catch (Exception e) { 
       e.printStackTrace(); 
       task.cancel(true); 
     } 
     return; 
} 
+1

! Task.cancelled() devrait être! IsCancelled() car djna a écrit à l'origine – Zed

+1

Je pense qu'il est préférable d'utiliser des interruptions. Tout d'abord, votre code appelable n'a pas besoin de connaître la 'tâche 'du tout. Deuxièmement, lorsque vous utilisez diverses opérations de blocage dans votre code appelable (comme 'Thread.sleep()'), elles ne réagissent pas à task.isCancelled(), mais réagissent généralement aux interruptions. Utiliser 'cancel (true)' et informer votre code des interruptions est généralement le meilleur moyen de le faire. (Votre code sera aussi plus général, car le mécanisme d'interruption est si largement utilisé dans Java) –

+0

Je suppose que mon point est que "l'interruption est un mécanisme coopératif". (http://www.ibm.com/developerworks/java/library/j-jtp05236.html) –

1

Une fois que vous avez pris le TimeoutException, vous devez appeler la méthode cancel (true) de votre tâche ...

ou fermer votre ExecutorService en appelant shutdownNow() ...

ou quitter la machine virtuelle en appelant System.exit (0)

en fonction de vos besoins

4

Votre appelable doit pouvoir s'arrêter rapidement, si nécessaire.

Votre code:

public Integer call() throws Exception { 
    for (int i=0; i < 1000000 && !task.cancelled(); i++) { 
     System.out.println(new java.util.Date()); 
     Thread.sleep(1000); // throws InterruptedException when thread is interrupted 
    } 
    return 0; 
} 

est déjà en mesure de le faire grâce à l'appel Thread.sleep(). Le point est que futureTask.cancel(true) va interrompre d'autres threads, et votre code doit réagir à cette interruption. Thread.sleep() fait cela. Si vous n'avez pas utilisé Thread.sleep() ou un autre code de blocage interruptible, vous devez vérifier vous-même Thread.currentThread().isInterrupted() et quitter le plus tôt possible (par exemple en lançant new InterruptedException()) lorsque vous trouvez cela vrai.

Vous devez appeler futureTask.cancel(true); à partir de votre gestionnaire d'exceptions pour annuler et interrompre le thread qui exécute votre tâche.

Mon conseil est de connaître le mécanisme d'interruption (c'est un excellent article: Dealing with InterruptedException), et l'utiliser.

Questions connexes