Maison  >  Article  >  Java  >  Exemples de framework Executor en Java

Exemples de framework Executor en Java

黄舟
黄舟original
2017-09-08 09:49:571761parcourir

Cet article présente principalement des informations pertinentes sur l'explication détaillée des exemples du framework Java Executor. Des exemples sont fournis ici pour aider tout le monde à apprendre et à comprendre cette partie du contenu. Les amis dans le besoin peuvent se référer au

. Framework Java Executor Explication détaillée des exemples

La plupart des accès simultanés sont obtenus grâce à l'exécution de tâches.

Il existe généralement deux manières d'effectuer des tâches : en série et en parallèle.


class SingleThreadWebServer {
 public static void main(String[] args) throws Exception {
  ServerSocket socket = new ServerSocket(80);
  while(true) {
   Socket conn = socket.accept();
   handleRequest(conn);
  }
 }
}
class ThreadPerTaskWebServer {
 public static void main(String[] args) throws Exception {
  ServerSocket socket = new ServerSocket(80);
  while(true) {
   final Socket conn = socket.accept();
   Runnable task = new Runnable() {
    public void run() {
     handleRequest(conn);
    }
   };
   new Thread(task).start();
  }
 }
}

Bien sûr, les deux méthodes ci-dessus posent des problèmes. Le problème avec le monothreading est que le niveau de concurrence constituera un goulot d'étranglement. Dans la version multithread, la création illimitée de threads entraînera des ressources insuffisantes.

Executor Framework

Une tâche est un ensemble d'unités de travail logiques et les threads sont un mécanisme qui permet aux tâches d'être exécutées de manière asynchrone.

JDK fournit l'interface Executor :


public interface Executor {
  void execute(Runnable command);
}

Bien que l'interface Executor soit relativement simple, elle constitue la base du framework d'exécution de tâches asynchrones, qui peut prendre en charge de nombreux types différents de stratégies d’exécution de tâches. Il fournit un moyen standard de dissocier le processus de soumission des tâches du processus d'exécution. Utilisez Runnable pour représenter les tâches. La mise en œuvre d'Executor fournit des mécanismes tels que la prise en charge du cycle de vie et la gestion des applications de statistiques.

Executor est basé sur le modèle producteur-consommateur. L'opération de soumission d'une tâche est équivalente au producteur, et le thread exécutant la tâche est équivalent au consommateur.

Les exemples de serveur Web basés sur l'exécuteur sont les suivants :


public class TaskExecutorWebServer {
 private static final int NTHREADS = 100;
 private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
 public static void main(String[] args) throws Exception {
  ServerSocket serverSocket = new ServerSocket(80);
  while (true) {
   final Socket conn = serverSocket.accept();
   Runnable task = new Runnable() {
    @Override
    public void run() {
     handleRequest(conn);
    }
   };
   exec.execute(task);
  }
 }
}

De plus, vous pouvez implémenter votre propre exécuteur pour contrôler s'il est simultané ou parallèle. , comme indiqué dans le code suivant :


/**
 * 执行已提交的 Runnable 任务的对象。
 * 此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。
 * 通常使用 Executor 而不是显式地创建线程。
 *
 *
 * @author renchunxiao
 *
 */
public class ExecutorDemo {
 public static void main(String[] args) {
  Executor executor = new ThreadExecutor();
  executor.execute(new Runnable() {
   @Override
   public void run() {
    // do something
   }
  });
  Executor executor2 = new SerialExecutor();
  executor2.execute(new Runnable() {
   @Override
   public void run() {
    // do something
   }
  });
 }
}
/**
 * 创建一个线程来执行 command
 *
 * @author renchunxiao
 *
 */
class ThreadExecutor implements Executor {
 @Override
 public void execute(Runnable command) {
  new Thread(command).start();
 }
}
/**
 * 串行执行 command
 *
 * @author renchunxiao
 *
 */
class SerialExecutor implements Executor {
 @Override
 public void execute(Runnable command) {
  command.run();
 }
}

Thread Pool

Thread Pool est un pool de ressources de threads, qui peut être créé via la méthode de fabrique statique dans Executors .

  • nouveauFixedThreadPool. Créez un pool de threads de longueur fixe et créez un thread chaque fois qu'une tâche est soumise jusqu'à ce que le nombre maximum de pools de threads soit atteint, la taille du pool de threads ne changera plus.

  • nouveauSingleThreadExecutor. Pool à thread unique.

  • newCachedThreadPool. Un pool de threads qui évolue en fonction de la taille de la tâche.

  • newScheduledThreadPool. Créez un pool de threads de longueur fixe pour effectuer des tâches de manière retardée ou chronométrée.

La JVM ne se fermera qu'après la fin de tous les threads non-démons. Par conséquent, si l'exécuteur ne peut pas être fermé correctement, la JVM ne peut pas se terminer.

Afin de résoudre le problème du cycle de vie des services d'exécution, il existe une nouvelle interface ExecutorService qui étend l'interface Executor.


public interface ExecutorService extends Executor {
 void shutdown();
 List<Runnable> shutdownNow();
 boolean isShutdown();
 boolean isTerminated();
 boolean awaitTermination(long timeout, TimeUnit unit)
  throws InterruptedException;
 <T> Future<T> submit(Callable<T> task);
 <T> Future<T> submit(Runnable task, T result);
 Future<?> submit(Runnable task);
 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
  throws InterruptedException;
 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
         long timeout, TimeUnit unit)
  throws InterruptedException;
 <T> T invokeAny(Collection<? extends Callable<T>> tasks)
  throws InterruptedException, ExecutionException;
 <T> T invokeAny(Collection<? extends Callable<T>> tasks,
     long timeout, TimeUnit unit)
  throws InterruptedException, ExecutionException, TimeoutException;
}

Le cycle de vie d'ExecutorService comporte trois états : en cours d'exécution, fermé et terminé. ExecutorService est en cours d'exécution lors de sa création initiale. La méthode d'arrêt s'arrêtera gracieusement : elle n'acceptera plus de nouvelles tâches et attendra la fin des tâches déjà exécutées (y compris celles qui n'ont pas encore commencé). La méthode shutdownNow effectuera un arrêt brutal : elle tentera d'annuler toutes les tâches en cours et ne démarrera plus les tâches de la file d'attente qui n'ont pas encore démarré. Une fois toutes les tâches terminées, il entre dans l'état terminé.

Callable et Future

Le framework Executor utilise Runnable comme représentation de base des tâches. Runnable est une abstraction limitée dans la mesure où sa méthode run ne peut pas renvoyer de valeur et lève une exception vérifiée.

De nombreuses tâches sont en réalité des calculs avec des délais, comme les requêtes de base de données et l'obtention de ressources du réseau. Pour ces tâches, Callable est une meilleure abstraction, qui suppose que call renverra une valeur et pourra lever une exception.

Les tâches exécutées par Executor comportent quatre étapes de cycle de vie : création, soumission, début et achèvement. Étant donné que certaines tâches prennent beaucoup de temps et peuvent devoir être annulées, dans le framework Executor, les tâches qui ont été soumises mais qui n'ont pas été démarrées peuvent être annulées.

Future représente le cycle de vie d'une tâche et fournit les méthodes correspondantes pour déterminer si elle a été terminée ou annulée, ainsi que pour obtenir les résultats de la tâche et annuler la tâche, etc.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn