Êtes-vous sûr d'utiliser le bon pool de threads Java ?

Auteur : Prince Poulet Rôti

Source : https://developer.hs.net/thread/2134

Récemment, je regardais le "Manuel de développement Java Edition Taishan" publié dans la [Liste des librairies des développeurs] d'Alibaba Cloud (je pense que beaucoup de frères devraient savoir que le manuel de développement d'Alibaba Cloud est en fait devenu un incontournable pour de nombreux développeurs et frères. J'ai lire le livre), il y a une spécification sur les threads dedans, et c'est obligatoire, regardons d'abord les instructions

1.png

[Obligatoire] Les pools de threads ne sont pas autorisés à utiliser des exécuteurs pour créer

Pourquoi désactiver ? C'est expliqué ci-dessous, car le nombre de threads ou la longueur de la file d'attente est fixé à la valeur maximale, ce qui peut provoquer un OOM dans certains cas. Certains jeunes frères du développement diront que je n'ai jamais vu de tels paramètres dans les exécuteurs. Découvrez son code source découvrir.

une brève introduction

Nous savons tous que 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 un outil pour exécuter des threads. La véritable interface du pool de threads est ExecutorService, qui est créée par Executor et retourne à ExecutorService, et il existe trois fonctions couramment utilisées (newWorkStealingPool, etc. sont rarement utilisées et ne seront pas expliquées), qui sont :

  • newFixedThreadPool crée un pool de threads de longueur fixe, qui peut contrôler le nombre maximal de threads simultanés, et les threads en excès attendront dans la file d'attente.
  • newSingleThreadExecutor crée un pool de threads à un seul thread qui n'utilise qu'un seul thread de travail pour exécuter des tâches, garantissant que toutes les tâches sont exécutées dans l'ordre spécifié (FIFO, LIFO, priorité).
  • newCachedThreadPool crée un pool de threads pouvant être mis en cache. Si la longueur du pool de threads dépasse les besoins de traitement, il peut recycler de manière flexible les threads inactifs. S'il n'y a pas de récupération, de nouveaux threads sont créés.

Description de ThreadPoolExecutor

Collez d'abord le code source de ThreadPoolExecutor :

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
  • corePoolSize => le nombre de threads principaux du pool de threads
  • maximumPoolSize => nombre maximum de pools de threads
  • keepAliveTime => temps de survie du thread inactif
  • unité => unité de temps
  • workQueue => la file d'attente tampon utilisée par le pool de threads
  • threadFactory => la fabrique utilisée par le pool de threads pour créer des threads
  • handler => la stratégie de traitement du pool de threads pour les tâches rejetées

Pour plus de détails, vous pouvez également consulter https://developer.hs.net/thread/2124

voir le code source

Tant de choses ont été dites ci-dessus, jetons un coup d'œil au code source, nous devrions le comprendre plus rapidement grâce au code source

newFixedThreadPool

Il y a deux méthodes newFixedThreadPool dans la classe Executors, mais la différence est qu'il y a un paramètre ThreadFactory supplémentaire, et ce paramètre a un impact limité sur les performances, donc nous ne le montrerons pas, seulement la première méthode de fonction

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  • corePoolSize = maximumPoolSize = nThreads, nThreads est un paramètre d'entrée, ce qui signifie qu'une grande valeur peut être entrée, ce qui peut provoquer un OOM
  • keepAliveTime = 0L, l'unité est la milliseconde
  • workQueue est LinkedBlockingQueue. Il convient de noter ici qu'en raison de l'utilisation de LinkedBlockingQueue, il est facile de provoquer des exceptions OOM lorsque les ressources sont limitées.

newSingleThreadExecutor

newSingleThreadExecutor est également une méthode de la classe Executors

public static ExecutorService newSingleThreadExecutor() {
     return new FinalizableDelegatedExecutorService (
        new ThreadPoolExecutor(1, 1, 0L, 
        TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); 
}

SingleThreadExecutor est un pool de threads à un seul thread avec un seul thread principal

  • corePoolSize = maximumPoolSize = 1
  • keepAliveTime = 0L, l'unité est la milliseconde
  • WorkQueue utilise LinkedBlockingQueue, alors il aura des problèmes avec newFixedThreadPool, et comme le nombre de threads traités est inférieur, la capacité de traitement est pire et cela provoquera un OOM.

newCachedThreadPool

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}
  • corePoolSize = 0, le nombre de pools de threads principaux est 0
  • maximumPoolSize = Integer.MAX_VALUE, le nombre maximal de pools de threads est Integer.MAX_VALUE.

Voyons quelle est la valeur de MAX_VALUE

public static final int   MAX_VALUE = 0x7fffffff;
  • keepAliveTime = 60L
  • l'unité est la seconde
  • workQueue est SynchronousQueue

Lorsqu'une tâche est soumise, corePoolSize est 0 et aucun thread principal n'est créé. SynchronousQueue est une file d'attente qui ne stocke pas d'éléments. On peut comprendre que la file d'attente est toujours pleine, de sorte que des threads non principaux seront éventuellement créés pour exécuter des tâches. Pour les threads non essentiels, ils seront recyclés lorsqu'ils seront inactifs pendant 60 secondes. Parce que Integer.MAX_VALUE est très grand, on peut considérer que les threads peuvent être créés à l'infini, ce qui est facile à provoquer des exceptions OOM dans le cas de ressources limitées

Résumer

De l'affichage ci-dessus, nous pouvons voir que

  • La longueur de file d'attente de requêtes autorisée pour FixedThreadPool et SingleThreadExecutor est Integer.MAX_VALUE, ce qui peut accumuler un grand nombre de requêtes, provoquant des exceptions OOM
  • Le nombre de threads que CachedThreadPool permet de créer est Integer.MAX_VALUE, ce qui peut créer un grand nombre de threads, provoquant des exceptions OOM

C'est pourquoi il est interdit d'utiliser des Executors pour créer des pools de threads, et il est recommandé de créer soi-même ThreadPoolExecutor

Bien sûr, les exécuteurs encapsulent réellement ThreadPoolExecutor, il est donc préférable d'utiliser directement ThreadPoolExecutor

{{o.name}}
{{m.name}}

Je suppose que tu aimes

Origine my.oschina.net/u/3620858/blog/5495608
conseillé
Classement