2016-10-24 2 views
0

J'ai une base d'application web (java) sur JSP. Dans cette application, je peux demander aux machines (PC) à propos de leur état et du système d'exploitation réel en fonction de leur IP en exécutant une commande externe.ExecutorService et OutOfMemoryError: impossible de créer un nouveau thread natif lors de l'utilisation de Executor

Pour accélérer la demande, j'ai pensé à demander plus de machines simultanément en utilisant des threads, c'est-à-dire ExecutorService.

L'écouteur preRenderView de la vue correspondante est défini sur cette méthode où je collecte toutes les données qui doivent être affichées. Ici, j'initialiser l'exécuteur testamentaire, qui est déclarée comme un champ de classe statique privée (private static ExecutorService executor):

public void selectData(ComponentSystemEvent event) 
{ 
    AmtRoomMachinesListController.executor = Executors.newFixedThreadPool(20); 

    AmtRoomMachinesListModel amtRoomMachinesListModel = (AmtRoomMachinesListModel)getModel(); 

    List<ListRow> listRows = fetchListRows(amtRoomMachinesListModel); 
... 
} 

dans fetchListRow l'exécuteur testamentaire est appelée et le appelable soumis. Ensuite, l'exécuteur est arrêté et mis fin:

private List<ListRow> fetchListRows(AmtRoomMachinesListModel amtRoomMachinesListModel) 
{ 
    ... 
    List<ListRow> listRows = Collections.synchronizedList(new ArrayList<ListRow>()); 

    for (Machine machine : room.getRoomPCs()) 
    { 
     executor.submit(new AmtcWorker(listRows, machine, amtRoomMachinesListModel)); 
    } 

    executor.shutdown(); 

    try 
    { 
     executor.awaitTermination(20, TimeUnit.SECONDS); 
    } 
    catch (InterruptedException e) 
    { 
     throw new BootrobotException(ExceptionType.AMTC_ERROR, "command", "Waiting for thread termination", "error", e.getMessage()); 
    } 

    ((ThreadPoolExecutor)executor).purge(); 

    LOGGER.info("Executor is shut down: " + executor.isShutdown()); 
    LOGGER.info("Executor is terminated: " + executor.isTerminated()); 

    sortListRows(listRows); 

    return listRows; 
} 

Mon problème est que le nombre de processus/threads augmente constamment et après un certain temps je reçois l'exception OutOfMemory. Chaque fois que le selectData est appelé, le nombre de processus augmente en fonction du nombre de machines invitées. Je suis une recrue avec un thread, mais je pensais que l'exécuteur s'occuperait des threads générés en les terminant/les supprimant quand executor.shutdown() ou executor.awaitTermination ou executor.purge() est appelé.

Qu'est-ce qui me manque?

+3

Création d'un exécuteur pour chaque demande est suspect. Il est probable que vous deviez créer un exécuteur et l'utiliser pour TOUTES les demandes. – OldCurmudgeon

+0

Avez-vous essayé d'annuler la référence après la fin de l'exécuteur? En dehors de cela, je seconde @OldCurmudgeon. Vous devriez probablement créer un ExecutorService "global" et utiliser [InvokeAll] (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html#invokeAll-java.util.Collection -long-java.util.concurrent.TimeUnit-). – Fildor

+1

Vous avez des instructions de journalisation dans votre code. Que montre le journal? Attendez-apparemment, votre 'executor' est une variable' static' dans votre classe que vous utilisez depuis les méthodes d'instance. C'est demander le chaos. Cette variable est écrasée chaque fois que 'selectData' est appelée, même si d'autres objets l'utilisent encore. Vous n'avez donc aucun contrôle sur l'exécuteur qui sera arrêté (ou à quelle fréquence) et qui ne s'arrêtera jamais. – Holger

Répondre

1

Ma suggestion est de créer un seul pool de threads. Le pool de threads est destiné à gérer vos threads. Si vous créez un pool de threads à chaque fois qu'une méthode est appelée, il est fondamentalement pire que de créer un thread chaque fois que vous appelez une méthode, mais si vous avez toujours insisté pour créer plus d'un pool de threads, pourquoi ne pas essayer au lieu

private List<ListRow> fetchListRows(AmtRoomMachinesListModel amtRoomMachinesListModel) 
{ 
    ExecutorService executor = Executors.newFixedThreadPool(20); 
... 
List<ListRow> listRows = Collections.synchronizedList(new ArrayList<ListRow>()); 

for (Machine machine : room.getRoomPCs()) 
{ 
    executor.submit(new AmtcWorker(listRows, machine, amtRoomMachinesListModel)); 
} 

executor.shutdown(); 

try 
{ 
    executor.awaitTermination(20, TimeUnit.SECONDS); 
} 
catch (InterruptedException e) 
{ 
    throw new BootrobotException(ExceptionType.AMTC_ERROR, "command", "Waiting for thread termination", "error", e.getMessage()); 
} 

((ThreadPoolExecutor)executor).purge(); 

LOGGER.info("Executor is shut down: " + executor.isShutdown()); 
LOGGER.info("Executor is terminated: " + executor.isTerminated()); 

sortListRows(listRows); 

return listRows; 
} 

juste localisé l'exécuteur

1

Utilisez un ThreadPool. En outre, avez-vous confirmé que la fonction awaitTermination retourne true? C'est une possibilité éloignée que awaitTermination ne se termine pas dans les 20 secondes et retourne false. Les nouveaux pools de threads continuent d'être créés sans que les plus anciens ne soient stockés dans le GCed et finissent par manquer de mémoire.