Résumé des questions d'entretien sur le pool de threads Java [Arrivage de stage 01]

SujetLocal

La clé est-elle nulle après GC ?

incertain.

(1) Lors de l'utilisation new ThreadLocal<>().set(s);pour définir threadlocal, si une variable n'est pas déclarée sur la pile pour pointer vers elle, alors elle n'est que faiblement référencée. Après gc, le threadlocal sera recyclé. Si la clé est nulle et que la valeur est toujours là, cela provoquera une fuite de mémoire. Par conséquent, assurez-vous d'appeler la méthode Remove après avoir utilisé le threadlocal pour éviter les fuites de mémoire car la clé est nulle. .

(2) Lorsqu'il est défini à l'aide ThreadLocal<object> threadLocal = new ThreadLocal<>();de la méthode, il équivaut à une variable threadLocal dans la pile pointant vers cet objet, formant une référence forte. Même dans l'entrée, en tant que clé, c'est une référence faible, mais il y a toujours une variable nommé "threadLocal" dans la pile. Il y a une référence forte pointant vers lui, donc la clé ne sera pas vide dans ce cas. Mais tant que la variable threadLocal disparaît après l'exécution de la méthode dans laquelle se trouve la variable, ne laissant qu'une référence faible, la clé sera toujours effacée et deviendra nulle.

Pourquoi la clé utilise-t-elle une référence faible ?

Si vous utilisez des références fortes, lorsque ThreadLocalla référence de l'objet (référence forte) est recyclée, ThreadLocalMapelle contient toujours ThreadLocalla référence forte. Si la clé n'est pas supprimée manuellement, elle ThreadLocalne sera pas recyclée. Par conséquent, tant que le thread actuel ne meurt pas, ThreadLocalMaples objets référencés ne seront pas recyclés.Il ne sera pas recyclé, ce qui peut être considéré comme provoquant Entryune fuite mémoire.

Mécanisme d'expansion ThreadLocalMap

A ThreadLocalMap.set()la fin de la méthode, si aucune donnée n'a été nettoyée après avoir effectué le travail de nettoyage heuristique et que le Entrynombre dans le tableau de hachage actuel a atteint le seuil d'expansion de la liste (len*2/3), la logique d'exécution démarrera rehash(): ici, le travail de nettoyage de détection sera être effectué en premier, tableen nettoyant à partir de la position de départ vers l'arrière, tablecertaines données peuvent être nettoyées. À ce stade, il est décidé d'augmenter ou non la capacité en keyjugeant .nullEntrysize >= threshold * 3/4

Jetons un coup d'œil à la resize()méthode spécifique. Pour faciliter la démonstration, la tabtaille développée est oldLen * 2, recalculez hashla position, puis placez-la dans un nouveau tabtableau. En cas hashde conflit, recherchez l' entryemplacement le plus proche null. Une fois le parcours terminé, oldTabtoutes les entrydonnées ont été placées dans le nouveau tab.

Lorsqu'elle est utilisée ThreadLocal, les données de copie de thread créées dans le thread parent ne peuvent pas être partagées avec le thread enfant dans un scénario asynchrone.

Afin de résoudre ce problème, il existe également une classe dans le JDK . Le principe d'implémentation est que le thread enfant est créé InheritableThreadLocalen appelant une méthode dans le thread parent , et la méthode est appelée dans le constructeur. Copiez les données du thread parent sur le thread enfant dans la méthode .new Thread()Thread#initThreadinit

ThreadLocalUtilisation pratique dans les projets

  1. Stocker le jeton utilisateur
  2. appel de lien traceId

Insérer la description de l'image ici

ThreadPoolExécuteur

Définition de la stratégie de saturation ThreadPoolExecutor

Si le nombre de threads en cours d'exécution simultanément atteint le nombre maximum de threads et que la file d'attente est pleine de tâches, ThreadPoolTaskExecutordéfinissez quelques stratégies :

  • ThreadPoolExecutor.AbortPolicy: (Par défaut) Lancer RejectedExecutionExceptionpour refuser le traitement des nouvelles tâches.
  • ThreadPoolExecutor.CallerRunsPolicy: Appel pour exécuter son propre thread pour exécuter la tâche, c'est-à-dire executeexécuter ( run) la tâche rejetée directement dans le thread qui a appelé la méthode. Si l'exécuteur a été fermé, la tâche sera supprimée. Par conséquent, cette stratégie réduira la vitesse de soumission des nouvelles tâches et affectera les performances globales du programme. Vous pouvez choisir cette stratégie si votre application peut tolérer ce délai et que vous exigez que chaque demande de tâche soit exécutée.
  • ThreadPoolExecutor.DiscardPolicy: Les nouvelles tâches ne sont pas traitées et supprimées directement.
  • ThreadPoolExecutor.DiscardOldestPolicy : Cette stratégie ignorera la demande de tâche non traitée la plus ancienne.

Deux façons de créer un pool de threads

Méthode 1 : ThreadPoolExecutorCréer via un constructeur (recommandé).

Méthode 2 : Créer via Executorla classe d'outils du framework .Executors

  1. FixedThreadPool:Cette méthode renvoie un pool de threads avec un nombre fixe de threads. Le nombre de threads dans le pool de threads reste toujours le même. Lorsqu'une nouvelle tâche est soumise, s'il y a des threads inactifs dans le pool de threads, elle sera exécutée immédiatement. Sinon, la nouvelle tâche sera temporairement stockée dans une file d'attente des tâches et lorsqu'un thread est inactif, la tâche dans la file d'attente des tâches sera traitée.
  2. SingleThreadExecutor: Cette méthode renvoie un pool de threads avec un seul thread. Si plusieurs tâches sont soumises au pool de threads, la tâche sera enregistrée dans une file d'attente des tâches. Lorsque le thread est inactif, les tâches de la file d'attente seront exécutées dans l'ordre premier entré, premier sorti.
  3. CachedThreadPool: Cette méthode renvoie un pool de threads qui peut ajuster le nombre de threads en fonction de la situation réelle. Le nombre de threads dans le pool de threads est incertain, mais s'il existe des threads inactifs qui peuvent être réutilisés, les threads réutilisables seront utilisés en premier. Si tous les threads fonctionnent et qu'une nouvelle tâche est soumise, un nouveau thread sera créé pour gérer la tâche. Une fois que tous les threads ont terminé l’exécution de la tâche en cours, ils seront renvoyés au pool de threads pour être réutilisés.
  4. ScheduledThreadPool: Ceci renvoie un pool de threads utilisé pour exécuter des tâches après un délai donné ou exécuter des tâches périodiquement.

La méthode 2 n’est pas recommandée car :

FixedThreadPoolEtSingleThreadExecutor : L'utilisation est illimitéeLinkedBlockingQueue , la longueur maximale de la file d'attente des tâches est de , et un grand nombre de requêtes peuvent s'accumuler, entraînant un MOO Integer.MAX_VALUE.

CachedThreadPool : Une file d'attente de synchronisation est utilisée SynchronousQueue, qui n'a pas de capacité. Lorsqu'il n'y a pas de threads inactifs, les threads sont immédiatement demandés. Le nombre de threads pouvant être créés est de . Integer.MAX_VALUEUn grand nombre de threads peuvent être créés, ce qui entraîne un MOO.

ScheduledThreadPoolEtSingleThreadScheduledExecutor : La file d'attente de blocage de délai illimité utilisée DelayedWorkQueues'étendra automatiquement à la moitié de la capacité d'origine lorsque les éléments ajoutés sont pleins, c'est-à-dire qu'elle ne bloquera jamais. La longueur maximale de la file d'attente des tâches est, et un grand nombre de requêtes Integer.MAX_VALUEpeuvent s'accumulent, ce qui entraîne un MOO.

DelayedWorkQueueLes éléments internes ne sont pas triés en fonction de l'heure à laquelle ils sont placés, mais les tâches sont triées en fonction de la durée du délai. La structure de données interne du " tas " est utilisée .

Analyse du principe du pool de threads

Insérer la description de l'image ici

Exécutable vs appelable

RunnableLes interfaces ne renvoient pas de résultats ni ne renvoient d'exceptions vérifiées, contrairement Callableaux interfaces.

La classe d'outils Executorspeut convertir Runnabledes objets en Callableobjets appartenant au modèle de convertisseur.

Différences entre soumettre et exécuter

execute()La méthode est utilisée pour soumettre des tâches qui ne nécessitent pas de valeur de retour, il est donc impossible de déterminer si la tâche est exécutée avec succès par le pool de threads ;

submit()La méthode est utilisée pour soumettre des tâches nécessitant des valeurs de retour. Le pool de threads renverra un objet FutureTaskFuture de type et obtiendra la valeur de retour via la méthode. La méthode bloquera le thread actuel jusqu'à ce que la tâche soit terminée . Si la méthode est utilisée , si la tâche n'a pas été exécutée dans le délai imparti, il sera jeté .Futureget()get()get(long timeout,TimeUnit unit)timeoutjava.util.concurrent.TimeoutException

shutdown() VS shutdownNow()

  • shutdown(): Fermez le pool de threads et l'état du pool de threads devient SHUTDOWN. Le pool de threads n'accepte plus de nouvelles tâches, mais les tâches dans la file d'attente doivent être terminées .
  • shutdownNow():Fermez le pool de Threads et l'état du thread change STOP. Le pool de threads mettra fin aux tâches en cours d'exécution, arrêtera le traitement des tâches en file d'attente et renverra la liste en attente d'exécution .

isTerminate() VS isShutdown()

  • isShutDownshutdown()Renvoie vrai lorsque la méthode est appelée .
  • isTerminatedLorsque shutdown()la méthode est appelée et que toutes les tâches soumises sont terminées, elle renvoie vrai

Comparaison entre ScheduledThreadPoolExecutor et Timer

  • TimerSensible aux changements de l'horloge système, ScheduledThreadPoolExecutornon ;
  • TimerIl n'y a qu'un seul thread d'exécution, donc les tâches de longue durée peuvent retarder les autres tâches. ScheduledThreadPoolExecutorN'importe quel nombre de threads peut être configuré. ThreadFactoryDe plus, vous pouvez avoir un contrôle total sur les threads créés si vous le souhaitez (en fournissant ;
    . ScheduledThreadPoolExecutorN'importe quel nombre de threads peut être configuré. ThreadFactoryDe plus, vous pouvez avoir un contrôle total sur les threads créés si vous le souhaitez (en fournissant ;
  • TimerTaskUne exception d'exécution lancée tuera un thread, entraînant une Timerpanique, c'est-à-dire que la tâche planifiée ne s'exécutera plus. ScheduledThreadExecutorNon seulement intercepte les exceptions d'exécution, mais vous permet également de les gérer si nécessaire (en remplaçant afterExecutela méthode ThreadPoolExecutor). La tâche qui lève l'exception sera annulée, mais les autres tâches continueront à s'exécuter.

Je suppose que tu aimes

Origine blog.csdn.net/m0_63323097/article/details/130590819
conseillé
Classement