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 ThreadLocal
la référence de l'objet (référence forte) est recyclée, ThreadLocalMap
elle contient toujours ThreadLocal
la référence forte. Si la clé n'est pas supprimée manuellement, elle ThreadLocal
ne sera pas recyclée. Par conséquent, tant que le thread actuel ne meurt pas, ThreadLocalMap
les objets référencés ne seront pas recyclés.Il ne sera pas recyclé, ce qui peut être considéré comme provoquant Entry
une 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 Entry
nombre 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, table
en nettoyant à partir de la position de départ vers l'arrière, table
certaines données peuvent être nettoyées. À ce stade, il est décidé d'augmenter ou non la capacité en key
jugeant .null
Entry
size >= threshold * 3/4
Jetons un coup d'œil à la resize()
méthode spécifique. Pour faciliter la démonstration, la tab
taille développée est oldLen * 2
, recalculez hash
la position, puis placez-la dans un nouveau tab
tableau. En cas hash
de conflit, recherchez l' entry
emplacement le plus proche null
. Une fois le parcours terminé, oldTab
toutes les entry
donné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éé InheritableThreadLocal
en 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#init
Thread
init
ThreadLocal
Utilisation pratique dans les projets
- Stocker le jeton utilisateur
- appel de lien traceId
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, ThreadPoolTaskExecutor
définissez quelques stratégies :
ThreadPoolExecutor.AbortPolicy
: (Par défaut) LancerRejectedExecutionException
pour refuser le traitement des nouvelles tâches.ThreadPoolExecutor.CallerRunsPolicy
: Appel pour exécuter son propre thread pour exécuter la tâche, c'est-à-direexecute
exé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 : ThreadPoolExecutor
Créer via un constructeur (recommandé).
Méthode 2 : Créer via Executor
la classe d'outils du framework .Executors
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.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.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.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 :
FixedThreadPool
EtSingleThreadExecutor
: 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 MOOInteger.MAX_VALUE
.
CachedThreadPool
: Une file d'attente de synchronisation est utiliséeSynchronousQueue
, 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_VALUE
Un grand nombre de threads peuvent être créés, ce qui entraîne un MOO.
ScheduledThreadPool
EtSingleThreadScheduledExecutor
: La file d'attente de blocage de délai illimité utiliséeDelayedWorkQueue
s'é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êtesInteger.MAX_VALUE
peuvent s'accumulent, ce qui entraîne un MOO.
DelayedWorkQueue
Les é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
Exécutable vs appelable
Runnable
Les interfaces ne renvoient pas de résultats ni ne renvoient d'exceptions vérifiées, contrairement Callable
aux interfaces.
La classe d'outils Executors
peut convertir Runnable
des objets en Callable
objets 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é .Future
get()
get()
get(long timeout,TimeUnit unit)
timeout
java.util.concurrent.TimeoutException
shutdown() VS shutdownNow()
shutdown()
: Fermez le pool de threads et l'état du pool de threads devientSHUTDOWN
. 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 changeSTOP
. 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()
isShutDown
shutdown()
Renvoie vrai lorsque la méthode est appelée .isTerminated
Lorsqueshutdown()
la méthode est appelée et que toutes les tâches soumises sont terminées, elle renvoie vrai
Comparaison entre ScheduledThreadPoolExecutor et Timer
Timer
Sensible aux changements de l'horloge système,ScheduledThreadPoolExecutor
non ;Timer
Il n'y a qu'un seul thread d'exécution, donc les tâches de longue durée peuvent retarder les autres tâches.ScheduledThreadPoolExecutor
N'importe quel nombre de threads peut être configuré.ThreadFactory
De plus, vous pouvez avoir un contrôle total sur les threads créés si vous le souhaitez (en fournissant ;
.ScheduledThreadPoolExecutor
N'importe quel nombre de threads peut être configuré.ThreadFactory
De plus, vous pouvez avoir un contrôle total sur les threads créés si vous le souhaitez (en fournissant ;TimerTask
Une exception d'exécution lancée tuera un thread, entraînant uneTimer
panique, c'est-à-dire que la tâche planifiée ne s'exécutera plus.ScheduledThreadExecutor
Non seulement intercepte les exceptions d'exécution, mais vous permet également de les gérer si nécessaire (en remplaçantafterExecute
la méthodeThreadPoolExecutor
). La tâche qui lève l'exception sera annulée, mais les autres tâches continueront à s'exécuter.