Gérer un pool de thread avec Spring 2.5

 

 
Dans ce post, je vais vous montrer comment utiliser les pools de Thread Java avec Spring 2.5. C’est d’une simplicité extrême.

 

Prérequis

– Connaissance Java;
– Maven installé, si ce n’est pas fait reportez vous à l’article installer Maven2 sous Linux pour les OS Linux ou installer Maven sous Windows pour les OS Windows;
– Eclipse installé.

 

Un pool de Thread, c’est quoi ?

Le pool de Thread permet de limiter les ressources d’une machine allouées pour application multithreadée donnée .

Si on ne contrôle pas le nombre de Threads utilisés, l’application créera autant de Threads qu’elle aura besoin.

Le nombre de Threads augmentant, les performances de la machine diminueront, jusqu’à l’écroulement des performances: la machine ne sera plus capable d’exécuter quoi que ce soit.

 

Création de notre projet MAVEN2

Une fois de plus, nous allons utiliser un archetype MAVEN2 pour nous simplifier la création de notre projet, via la commande :

mvn archetype:generate

.

L’archetype utilisé est maven-archetype-quickstart. Pour l’utilisation des archetypes, référez-vous à l’article Maven et les archetypes. Pour l’exemple, choisissez comme artefactId (nom de votre projet) ThreadPoolTest.

Vous pouvez créer le projet en invoquant directement la commande :

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

 

Importation de notre projet MAVEN sous Eclipse

Dans un terminal, placez dans le dossier de votre projet ThreadPoolTest et exécutez la commande MAVEN suivante :

mvn clean eclipse:clean eclipse:eclipse

Il ne reste plus qu’à importer le projet sous Eclipse en passant par le menu File/Import.

 

Les dépendances MAVEN2

Editez le fichier pom.xml, et ajoutez la dépendance suivante entre les balises dependencies :

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring</artifactId>
 <version>2.5.6</version>
</dependency>

Mettez à jour vos dépendances en exécutant la commande :

mvn clean eclipse:clean eclipse:eclipse

 

Configuration SPRING

Il existe de 3 propriétés servant à configurer proprement le pool et qui régissent son mode de fonctionnement.

On peut résumer en quatre règles l’utilisation de ces 3 paramètres :
– le pool crée un nouveau Thread pour lancer une tâche, si le nombre de threads en exécution est inférieur à corePoolSize;
– le pool crée un nouveau Thread pour lancer une tâche, si la file d’attente est pleine (queueCapacity atteint) et que le nombre de threads est inférieur à maxPoolSize;
– une tâche est mise dans la file d’attente du pool, si le nombre de threads en exécution est supérieur ou égal à corePoolSize;
– le pool rejette une tâche, si la file d’attente est pleine (queueCapacity atteint) et que le nombre de threads est supérieur ou égal à maxPoolSize.

Pour la configuration, il suffit d’intégrer dans le fichier applicationContext.xml le bean ou code suivant avec les paramètres adaptés à votre infrastructure.

 <bean id="taskExecutor"
  class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
   <property name="corePoolSize" value="5"/>
   <property name="maxPoolSize" value="5"/>
   <property name="queueCapacity" value="10" />
 </bean>

 

L’exemple

Le code source de la tâche

La tâche est représentée par une classe. Pour que cette classe puisse être exécutée via un thread, elle doit implémenter l’interface Runnable.

Cette interface oblige à implémenter une méthode void run() qui comporte le code exécuté par le thread.

Pour notre exemple, notre tâche effectuera une pause dont la durée aléatoire variera entre 5 et 15 secondes. Elle affichera l’entrée de la méthode Run, sa sortie, et son temps de pause.

package thread;
 
import java.util.Random;
 
public class TestTask implements Runnable
{
  public Integer threadNumber;
 
  public TestTask(int i)
  {
    this.threadNumber = i;
  }
 
  @Override
  public void run()
  {
    System.out.println("Thread start " + threadNumber);
    // Génération d'un nombre aléatoire entre
    // 5s et 15s pour le temps de pause du Thread
    Random r = new Random();
    int valeur = 5000 + r.nextInt(15000 - 5000);
    System.out.println("Thread pause " + threadNumber + " " + valeur);
    try 
    {
      Thread.sleep(valeur);
    } 
    catch (InterruptedException e) 
    {
      e.printStackTrace();
    }
    System.out.println("Thread stop" + threadNumber);
  }
 
  public void setThreadNumber(Integer threadNumber)
  {
    this.threadNumber = threadNumber;
  }
}

 

Le code source du lanceur de tâches

Le lanceur est chargé d’assigner les tâches au pool qui les gèrera conformément à la configuration qui lui est imposée.

Ceci se fait par le biais d’une classe java composée de la classe ThreadPoolTaskExecutor, le pool de thread.

Si le pool n’est pas en capacité de prendre en charge une tâche (exécution, mise en file d’attente), celle-ci sera rejetée et une exception sera lancée. Dans l’exemple suivant, nous la levons de manière à tracer le rejet de la tâche.

La méthode setWaitForTasksToCompleteOnShutdown en prenant en paramètre un booléen indique (valeur true) ou pas (valeur false) au pool d’attendre que toutes les tâches en exécution soient terminées avant de s’arrêter.

package thread;
 
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
public class LauncherTestTask
{
  private ThreadPoolTaskExecutor taskExecutor;
 
  public void launch()
  {
    taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    for (int i=1 ; i<30 ; i++)
    {
      try
      {
        taskExecutor.execute(new TestTask(i) );
      } catch (Exception e)
      {
        System.out.println("T" + i + " rejeté par le Pool");
      }
    }
    taskExecutor.shutdown();
  }
 
  public void setTaskExecutor(ThreadPoolTaskExecutor taskExecutor)
  {
    this.taskExecutor = taskExecutor;
  }
}

 

Le fichier applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd"
   default-autowire="byName">
 <bean id="taskExecutor"
  class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
   <property name="corePoolSize" value="5"/>
   <property name="maxPoolSize" value="5"/>
   <property name="queueCapacity" value="10" />
 </bean>
 <bean id="launcherTestTask" class="thread.LauncherTestTask">
  <property name="taskExecutor">
   <ref bean="taskExecutor" />
  </property>
 </bean>
</beans>

 

Lancement de l’exemple

Grâce au fichier applicationContext.xml, Spring va nous permettre de récupérer une instance du lanceur, puis d’en invoquer la méthode launch.

Le chargement du fichier applicationContext.xml se fait par le biais de la classe ClassPathXmlApplicationContext, dont le constructeur prend en paramètre le nom du fichier de contexte (applicationContext.xml dans le cas présent).

La récupération de l’instance de la classe dont nous avons besoin se fait par la biais de la méthode getBean qui prend en paramètre le nom du bean (bean id) défini dans le fichier de contexte.

Le code source:

package Thread;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Run 
{
  public static void main(String[] args) throws InterruptedException 
  {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    LauncherTestTask launcherTestTask = (LauncherTestTask)ctx.getBean("launcherTestTask");
    launcherTestTask.launch();
  }
}

 

Limite de notre exemple

Dans l’exemple ci-dessus, si vous souhaitez lancer un nombre de tâches plus important que la capacité du Pool, il n’y aura pas de blocage mais beaucoup d’entre elles seront rejetées.

Pour éviter ce problème, il va falloir utiliser des sémaphores, sujet du prochain article sur les pools de threads.

 
[important]Vous savez maintenant utiliser un pool de Thread en Java, il ne reste plus qu’à en faire bon usage. N’hésitez pas à poser vos questions par le biais des commentaires.[/important]

 

Posté dans javaTaggé java thread, pool de thread, pool de thread java, thread, thread java, thread pool, thread pool java  |  Laisser un commentaire

Répondre