Maison > Java > javaDidacticiel > le corps du texte

Qu'est-ce que le pool de threads Java ? Explication détaillée du pool de threads Java

不言
Libérer: 2018-09-19 15:13:35
original
2081 Les gens l'ont consulté

Le contenu de cet article porte sur ce qu'est le pool de threads Java ? L'explication détaillée du pool de threads Java a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer, j'espère qu'elle vous sera utile.

1. Qu'est-ce qu'un pool de threads :

java.util.concurrent.Executors fournit une implémentation de l'interface java.util.concurrent.Executor pour créer un thread pool

La technologie multi-thread résout principalement le problème de l'exécution de plusieurs threads dans l'unité de processeur. Elle peut réduire considérablement le temps d'inactivité de l'unité de processeur et augmenter la capacité de débit de l'unité de processeur.

Supposons que le temps nécessaire à un serveur pour terminer une tâche soit : le temps T1 pour créer un thread, le temps T2 pour exécuter la tâche dans le thread et le temps T3 pour détruire le thread.

Si : T1 + T3 est beaucoup plus grand que T2, vous pouvez utiliser un pool de threads pour améliorer les performances du serveur.

Un pool de threads comprend les quatre composants de base suivants :

  • 1. , y compris la création d'un pool de threads, la destruction du pool de threads, l'ajout de nouvelles tâches

  • 2. Thread de travail (PoolWorker) : les threads du pool de threads sont dans un état d'attente lorsqu'il n'y a aucune tâche. Exécution cyclique des tâches ;

  • 3. Interface de tâche (Task) : L'interface que chaque tâche doit implémenter pour que le thread de travail planifie l'exécution de la tâche. point d'entrée de la tâche. Le travail de finition après l'exécution de la tâche, l'état d'exécution de la tâche, etc.

  • 4. File d'attente des tâches (taskQueue) : utilisée pour stocker les tâches non traitées. . Fournir un mécanisme tampon.

La technologie du pool de threads est une technologie qui se concentre sur la façon de raccourcir ou d'ajuster les temps T1 et T3, améliorant ainsi les performances du programme serveur. Il organise T1 et T3 respectivement dans les périodes de démarrage et de fin du programme serveur ou dans certaines périodes d'inactivité, de sorte que lorsque le programme serveur traite les demandes des clients, il n'y aura pas de surcharge de T1 et T3.

Le pool de threads ajuste non seulement la période de temps pendant laquelle T1 et T3 sont générés, mais il réduit également considérablement le nombre de threads créés. Jetez un œil à un exemple :

Supposons un serveur pendant une journée. Il y a 50 000 requêtes à traiter et chaque requête nécessite un thread distinct pour être complétée. Dans le pool de threads, le nombre de threads est généralement fixe, donc le nombre total de threads générés ne dépassera pas le nombre de threads dans le pool de threads. Si le serveur n'utilise pas le pool de threads pour traiter ces requêtes, le nombre total de. les fils seront de 50 000. La taille générale du pool de threads est bien inférieure à 50 000. Par conséquent, le programme serveur qui utilise le pool de threads ne perdra pas de temps à traiter les requêtes afin d'en créer 50 000, améliorant ainsi l'efficacité.

2. Pools de threads communs

①newSingleThreadExecutor

Un pool de threads à un seul thread, c'est-à-dire qu'un seul thread fonctionne dans le pool de threads à la fois. , et une seule chaîne de thread Tâches d'exécution de ligne

②newFixedThreadExecutor(n)

Un nombre fixe de pools de threads Si une tâche n'est pas soumise, ce sera un thread. jusqu'à ce que le nombre maximum de pools de threads soit atteint, puis entrez plus tard. La file d'attente ne poursuivra pas l'exécution tant que la tâche précédente n'est pas terminée

③newCacheThreadExecutor (recommandé)

Cacheable pool de threads, Lorsque la taille du pool de threads dépasse la tâche de traitement. Si les threads requis sont nécessaires, alors certains threads inactifs (généralement aucune exécution pendant 60 secondes) seront recyclés. Lorsqu'une tâche arrive, de nouveaux threads seront recyclés. intelligemment ajouté pour l’exécution.

④newScheduleThreadExecutor

Pool de Threads illimité, prenant en charge les threads d'exécution planifiés et périodiques

Le pool de Threads fourni par Java est plus puissant,Je crois que si vous comprenez le principe de fonctionnement du pool de threads, vous ne vous sentirez pas étranger lorsque vous examinerez le pool de threads dans la bibliothèque de classes.

Quest-ce que le pool de threads Java ? Explication détaillée du pool de threads Java

Quest-ce que le pool de threads Java ? Explication détaillée du pool de threads Java

Article 2 :

Instructions d'utilisation du pool de threads Java

Une introduction

L'utilisation des threads occupe une position extrêmement importante en Java Dans jdk1.4 et les versions précédentes de jdk, il existe de nombreuses informations sur. pools de threads C'est extrêmement grossier à utiliser. Cette situation a beaucoup changé après jdk1.5. Après Jdk1.5, le package java.util.concurrent a été ajouté. Ce package introduit principalement l'utilisation de threads et de pools de threads en Java. Cela nous aide beaucoup à résoudre les problèmes de threads lors du développement.

2 : Pool de threads

Le rôle du pool de threads :

La fonction du pool de threads est de limiter le nombre de threads d'exécution dans le système.
Selon l'environnement du système, le nombre de threads peut être défini automatiquement ou manuellement pour obtenir le meilleur effet de fonctionnement ; moins de ressources système seront gaspillées, et davantage entraînera une congestion et une inefficacité du système. Utilisez le pool de threads pour contrôler le nombre de threads, et les autres threads sont mis en file d'attente pour attendre. Une fois qu'une tâche est exécutée, la tâche la plus en avant dans la file d'attente est prise et l'exécution commence. S'il n'y a aucun processus en attente dans la file d'attente, cette ressource du pool de threads est en attente. Lorsqu'une nouvelle tâche doit être exécutée, s'il y a des threads de travail en attente dans le pool de threads, elle peut commencer à s'exécuter, sinon elle entre dans la file d'attente ;

Pourquoi utiliser le pool de threads :

1. Réduisez le nombre de créations et de destructions de threads, chaque thread de travail peut être réutilisé et peut effectuer plusieurs tâches.

2. Vous pouvez ajuster le nombre de threads de travail dans le pool de threads en fonction de la capacité du système pour éviter que le serveur ne soit épuisé en raison d'une consommation excessive de mémoire (chaque thread nécessite environ 1 Mo de mémoire et le thread démarre Plus la mémoire est consommée, plus la mémoire sera consommée et éventuellement le système plantera).

L'interface de niveau supérieur du pool de threads en Java est Executor, mais à proprement parler, Executor n'est pas un pool de threads, mais juste un outil pour exécuter des threads. La véritable interface du pool de threads est ExecutorService.

Certaines des catégories les plus importantes :

Classification Fonction
分类 作用
ExecutorService 真正的线程池接口。
ScheduledExecutorService 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
ThreadPoolExecutor ExecutorService的默认实现。
ScheduledThreadPoolExecutor 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
ExecutorService
Véritable interface de pool de threads.
ScheduledExecutorService peut être similaire à Timer/TimerTask et résoudre des problèmes qui nécessitent l'exécution répétée de tâches.
ThreadPoolExecutor L'implémentation par défaut d'ExecutorService.
ScheduledThreadPoolExecutor Hérite de l'implémentation de l'interface ScheduledExecutorService de ThreadPoolExecutor, une implémentation de classe de planification de tâches périodiques.

Il est relativement compliqué de configurer un pool de threads, surtout si le principe du pool de threads n'est pas très clair, il est très probable que le pool de threads configuré ne soit pas optimal, donc quelques fabriques statiques sont fournies dans la classe Executors. Générez des pools de threads couramment utilisés.

1. newSingleThreadExecutor

Créez un pool de threads à un seul thread. Ce pool de threads n'a qu'un seul thread fonctionnel, ce qui équivaut à un seul thread exécutant toutes les tâches en série. Si le seul thread se termine anormalement, un nouveau thread le remplacera. Ce pool de threads garantit que toutes les tâches sont exécutées dans l'ordre dans lequel elles sont soumises.

2.newFixedThreadPool

Créez un pool de threads de taille fixe. Un thread est créé à chaque fois qu'une tâche est soumise, jusqu'à ce que le thread atteigne la taille maximale du pool de threads. La taille du pool de threads reste inchangée une fois qu'elle atteint la valeur maximale. Si un thread se termine en raison d'une exception d'exécution, le pool de threads sera reconstitué avec un nouveau thread.

3. newCachedThreadPool

Créez un pool de threads pouvant être mis en cache. Si la taille du pool de threads dépasse le nombre de threads requis pour traiter la tâche,

alors certains threads inactifs (n'exécutant pas de tâches pendant 60 secondes) seront recyclés. Lorsque le nombre de tâches augmente, ce pool de threads peut être recyclé. ajouté intelligemment Nouveau fil pour gérer la tâche. Ce pool de threads ne limite pas la taille du pool de threads. La taille du pool de threads dépend entièrement de la taille maximale des threads que le système d'exploitation (ou la JVM) peut créer.

4.newScheduledThreadPool

Créez un pool de threads de taille illimitée. Ce pool de threads prend en charge la synchronisation et l'exécution périodique des tâches.

Instance

1 : newSingleThreadExecutor

package com.thread;
 /* * 
  *通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class MyThread extends Thread { 

    @Override public void run() { // TODO Auto-generated method stub // super.run();
    System.out.println(Thread.currentThread().getName()+"正在执行....");
    } 
}
Copier après la connexion
package com.thread; 
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; 
/* 
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class singleThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newSingleThreadExecutor(); //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
 Thread t1=new MyThread();

        Thread t2=new MyThread();

        Thread t3=new MyThread();

        Thread t4=new MyThread();

        Thread t5=new MyThread(); //将线程放到池中执行;
 pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5); //关闭线程池
 pool.shutdown();

    }

}
Copier après la connexion

Résultat :

pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
Copier après la connexion

2newFixedThreadPool

 package com.thread; 
import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors; 
/* * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class fixedThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newFixedThreadPool(2); //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
 Thread t1=new MyThread();

        Thread t2=new MyThread();

        Thread t3=new MyThread();

        Thread t4=new MyThread();

        Thread t5=new MyThread(); //将线程放到池中执行;
 pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5); //关闭线程池
 pool.shutdown();

    }

}
Copier après la connexion

Résultat :

pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-2正在执行....
Copier après la connexion

3, newCachedThreadPool

package com.thread;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 /* 
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class cachedThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newCachedThreadPool(); //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
 Thread t1=new MyThread();

        Thread t2=new MyThread();

        Thread t3=new MyThread();

        Thread t4=new MyThread();

        Thread t5=new MyThread(); //将线程放到池中执行;
 pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5); //关闭线程池
 pool.shutdown();

    }

}
Copier après la connexion

Résultat :

pool-1-thread-2正在执行....
pool-1-thread-1正在执行....
pool-1-thread-3正在执行....
pool-1-thread-4正在执行....
pool-1-thread-5正在执行....
Copier après la connexion

4.newScheduledThreadPool

package com.thread; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; 
/* * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class scheduledThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub
 ScheduledThreadPoolExecutor exec =new ScheduledThreadPoolExecutor(1);
       exec.scheduleAtFixedRate(new Runnable(){//每隔一段时间就触发异常
 @Override public void run() { // TODO Auto-generated method stub //throw new RuntimeException();
            System.out.println("===================");

        }}, 1000, 5000, TimeUnit.MILLISECONDS);  

       exec.scheduleAtFixedRate(new Runnable(){//每隔一段时间打印系统时间,证明两者是互不影响的
 @Override public void run() { // TODO Auto-generated method stub
 System.out.println(System.nanoTime());

        }}, 1000, 2000, TimeUnit.MILLISECONDS);

    }

}
Copier après la connexion

Résultat :

===================
23119318857491
23121319071841
23123319007891
===================
23125318176937
23127318190359
===================
23129318176148
23131318344312
23133318465896
===================
23135319645812
Copier après la connexion

3 : Explication détaillée de ThreadPoolExecutor

Le complet structure de ThreadPoolExecutor La signature de la méthode est : ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)</runnable> .

corePoolSize - Le nombre de threads enregistrés dans le pool, y compris les threads inactifs.

maximumPoolSize - Le nombre maximum de threads autorisés dans le pool.

keepAliveTime - Lorsque le nombre de threads est supérieur au cœur, il s'agit du temps maximum pendant lequel les threads inactifs en excès attendent de nouvelles tâches avant de se terminer.

unit - L'unité de temps du paramètre keepAliveTime.

workQueue - Une file d'attente utilisée pour conserver les tâches avant leur exécution. Cette file d'attente contient uniquement les tâches Runnable soumises par la méthode d'exécution.

threadFactory - La fabrique utilisée par l'exécuteur pour créer de nouveaux threads.

handler - Gestionnaire utilisé lorsque l'exécution est bloquée en raison d'un dépassement de la portée du thread et de la capacité de la file d'attente.

ThreadPoolExecutor est l'implémentation sous-jacente de la classe Executors.

Dans le document d'aide du JDK, il y a un tel passage :

"Il est fortement recommandé aux programmeurs d'utiliser la Executorsméthode d'usineExecutors.newCachedThreadPool() plus pratique (pool de threads illimité , peut être un recyclage automatique des threads), Executors.newFixedThreadPool(int) (pool de threads de taille fixe) Executors.newSingleThreadExecutor() (un seul fil d'arrière-plan)

Ils ont tous des paramètres prédéfinis pour la plupart des scénarios d'utilisation "

Présentés ci-dessous. . Code source de plusieurs classes :

ExecutorService newFixedThreadPool (int nThreads) : Pool de threads de taille fixe.

Comme vous pouvez le constater, les tailles de corePoolSize et maximumPoolSize sont les mêmes (en fait, comme nous le verrons plus tard, le paramètre maximumPoolSize n'a aucun sens si une file d'attente illimitée est utilisée), et les noms de table de définition de keepAliveTime et de l'unité Quoi ? -Il est temps de réaliser que tu ne veux pas rester en vie ! Le dernier BlockingQueue a choisi LinkedBlockingQueue, qui a la particularité d'être illimité.

public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>());   

      }</runnable>
Copier après la connexion

ExecutorService newSingleThreadExecutor() : thread unique

public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService   
                 (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>()));   
       }</runnable>
Copier après la connexion

ExecutorService newCachedThreadPool() : pool de threads illimité, le recyclage automatique des threads est possible

Cette implémentation est intéressante. Le premier est le pool de threads illimité, nous pouvons donc constater que maximumPoolSize est grand. Deuxièmement, SynchronousQueue est utilisé dans la sélection de BlockingQueue. Vous n'êtes peut-être pas familier avec ce BlockingQueue. En termes simples : dans cette QUEUE, chaque opération d'insertion doit attendre l'opération de suppression correspondante d'un autre thread.

 public static ExecutorService newCachedThreadPool() {
   return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<runnable>());   
    }</runnable>
Copier après la connexion

Commençons par le paramètre BlockingQueue Dans le JDK, il a été clairement indiqué qu'il existe trois types de files d'attente.

Toutes les BlockingQueue peuvent être utilisées pour transférer et conserver les tâches soumises. Cette file d'attente peut être utilisée pour interagir avec la taille du pool :

如果运行的线程少于 corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行)

如果运行的线程等于或多于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程

如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

queue上的三种类型。

排队有三种通用策略:

直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

有界队列。当使用有限的 maximumPoolSizes时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。  

BlockingQueue的选择。

例子一:使用直接提交策略,也即SynchronousQueue。

首先SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。在这里不是核心线程便是新创建的线程,但是我们试想一样下,下面的场景。

我们使用一下参数构造ThreadPoolExecutor:

 new ThreadPoolExecutor( 2, 3, 30, TimeUnit.SECONDS, new  SynchronousQueue<runnable>(), new RecorderThreadFactory("CookieRecorderPool"), new ThreadPoolExecutor.CallerRunsPolicy());</runnable>
Copier après la connexion

当核心线程已经有2个正在运行.

  1. 此时继续来了一个任务(A),根据前面介绍的“如果运行的线程等于或多于 corePoolSize,则Executor始终首选将请求加入队列,而不添加新的线程。”,所以A被添加到queue中。

  2. 又来了一个任务(B),且核心2个线程还没有忙完,OK,接下来首先尝试1中描述,但是由于使用的SynchronousQueue,所以一定无法加入进去。

  3. 此时便满足了上面提到的“如果无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。”,所以必然会新建一个线程来运行这个任务。

  4. 暂时还可以,但是如果这三个任务都还没完成,连续来了两个任务,第一个添加入queue中,后一个呢?queue中无法插入,而线程数达到了maximumPoolSize,所以只好执行异常策略了。

所以在使用SynchronousQueue通常要求maximumPoolSize是无界的,这样就可以避免上述情况发生(如果希望限制就直接使用有界队列)。对于使用SynchronousQueue的作用jdk中写的很清楚:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。

什么意思?如果你的任务A1,A2有内部关联,A1需要先运行,那么先提交A1,再提交A2,当使用SynchronousQueue我们可以保证,A1必定先被执行,在A1么有被执行前,A2不可能添加入queue中。

例子二:使用无界队列策略,即LinkedBlockingQueue

这个就拿newFixedThreadPool来说,根据前文提到的规则:

如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。那么当任务继续增加,会发生什么呢?

如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。OK,此时任务变加入队列之中了,那什么时候才会添加新线程呢?

如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。这里就很有意思了,可能会出现无法加入队列吗?不像SynchronousQueue那样有其自身的特点,对于无界队列来说,总是可以加入的(资源耗尽,当然另当别论)。换句说,永远也不会触发产生新的线程!corePoolSize大小的线程数会一直运行,忙完当前的,就从队列中拿任务开始运行。所以要防止任务疯长,比如任务运行的实行比较长,而添加任务的速度远远超过处理任务的时间,而且还不断增加,不一会儿就爆了。

例子三:有界队列,使用ArrayBlockingQueue。

这个是最为复杂的使用,所以JDK不推荐使用也有些道理。与上面的相比,最大的特点便是可以防止资源耗尽的情况发生。

举例来说,请看如下构造方法:

 new ThreadPoolExecutor( 2, 4, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<runnable>(2), new RecorderThreadFactory("CookieRecorderPool"), new ThreadPoolExecutor.CallerRunsPolicy());</runnable>
Copier après la connexion

假设,所有的任务都永远无法执行完。

对于首先来的A,B来说直接运行,接下来,如果来了C,D,他们会被放到queue中,如果接下来再来E,F,则增加线程运行E,F。但是如果再来任务,队列无法再接受了,线程数也到达最大的限制了,所以就会使用拒绝策略来处理。

keepAliveTime

jdk中的解释是:当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

有点拗口,其实这个不难理解,在使用了“池”的应用中,大多都有类似的参数需要配置。比如数据库连接池,DBCP中的maxIdle,minIdle参数。

什么意思?接着上面的解释,后来向老板派来的工人始终是“借来的”,俗话说“有借就有还”,但这里的问题就是什么时候还了,如果借来的工人刚完成一个任务就还回去,后来发现任务还有,那岂不是又要去借?这一来一往,老板肯定头也大死了。

合理的策略:既然借了,那就多借一会儿。直到“某一段”时间后,发现再也用不到这些工人时,便可以还回去了。这里的某一段时间便是keepAliveTime的含义,TimeUnit为keepAliveTime值的度量。

RejectedExecutionHandler

另一种情况便是,即使向老板借了工人,但是任务还是继续过来,还是忙不过来,这时整个队伍只好拒绝接受了。

RejectedExecutionHandler接口提供了对于拒绝任务的处理的自定方法的机会。在ThreadPoolExecutor中已经默认包含了4中策略,因为源码非常简单,这里直接贴出来。

CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) {

               r.run();

           }

       }
Copier après la connexion

这个策略显然不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。

AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException

 这种策略直接抛出异常,丢弃任务。

DiscardPolicy:不能执行的任务将被删除

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

       }
Copier après la connexion

 这种策略和AbortPolicy几乎一样,也是丢弃任务,只不过他不抛出异常。

DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
if (!e.isShutdown()) {

               e.getQueue().poll();

               e.execute(r);

           }

       }
Copier après la connexion

该策略就稍微复杂一些,在pool没有关闭的前提下首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。这个策略需要适当小心。

设想:如果其他线程都还在运行,那么新来任务踢掉旧任务,缓存在queue中,再来一个任务又会踢掉queue中最老任务。

总结:

keepAliveTime和maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的,那么永远不会触发maximumPoolSize,自然keepAliveTime也就没有了意义。

反之,如果核心数较小,有界BlockingQueue数值又较小,同时keepAliveTime又设的很小,如果任务频繁,那么系统就会频繁的申请回收线程。

public static ExecutorService newFixedThreadPool(int nThreads) {
 return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>());
}</runnable>
Copier après la connexion

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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!