当前位置:网站首页>Référence forte, faible, douce et virtuelle de threadlocal
Référence forte, faible, douce et virtuelle de threadlocal
2022-06-27 14:28:00 【Il faut du temps aux poissons pour trouver de l'eau.】
1.ThreadLocalIntroduction
ThreadLocalFournir des variables locales de thread. Ces variables sont comparées aux variables normales C'est différent.,Parce que chaque fil accèdeThreadLocalPar exemple(Par songetOusetMéthodes)Ils ont tous leurs propres、Copie de la variable initialisée indépendamment.ThreadLocalLes instances sont généralement des champs statiques privés dans une classe,Le but de l'utiliser est de rendre l'état(Par exemple,UtilisateursIDOu affairesID)Associé à un thread.
RéalisationChaque thread a sa propre copie de la variable locale(Utilisez vos propres variables pour ne pas déranger les autres,Ne pas partager avec les autres,Tout le monde a sa part.,Un pour chaque personne),La solution principale est de lier chaque thread à sa propre valeur,En utilisantget() Etset() Méthodes,Obtient la valeur par défaut ou change sa valeur à la valeur de la copie enregistrée par le thread courant pour éviter les problèmes de sécurité du thread.
2.Pour toujours.helloworld
class House //Catégorie de ressources
{
int saleCount = 0;
public synchronized void saleHouse() {
++saleCount;
}
/*ThreadLocal<Integer> saleVolume = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 0; } };*/
ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
public void saleVolumeByThreadLocal() {
saleVolume.set(1 + saleVolume.get());
}
}
/** * Besoins1: 5 Vente de maisons , Les dirigeants du Groupe ne se soucient que des statistiques exactes du volume total des ventes. . * <p> * Besoins2: 5 Nombre aléatoire de maisons vendues , Limite de vente individuelle , Le rendement est calculé sur la base de la Commission , */
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
House house = new House();
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
int size = new Random().nextInt(5) + 1;
try {
for (int j = 1; j <= size; j++) {
house.saleHouse();
house.saleVolumeByThreadLocal();
}
System.out.println(Thread.currentThread().getName() + "\t" + " Vendu le :" + house.saleVolume.get());
} finally {
house.saleVolume.remove();
}
}, String.valueOf(i)).start();
}
//Pause milliseconde
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + " Combien d'ensembles ont été vendus au total : " + house.saleCount);
}
}
Petit résumé:
Parce que chaque Thread Il y en a un à l'intérieur.Copie de l'InstanceEt cette copie n'est utilisée que par le thread courant lui - même
Puisque les autres Thread Inaccessible,Alors il n'y a pas de problème de partage multithreadé.
Définir uniformément les valeurs initiales,Mais chaque thread modifie cette valeur indépendamment l'un de l'autre
- AdhésionsynchronizedOuLockContrôler l'ordre d'accès aux ressources
- Une personne.,Tout le monde va bien,Pas besoin de voler
Ali!ThreadLocalDébut de la spécification
class MyData
{
ThreadLocal<Integer> threadLocalField = ThreadLocal.withInitial(() -> 0);
public void add()
{
threadLocalField.set(1 + threadLocalField.get());
}
}
/** .【Obligatoire】Les personnalisations doivent être recyclées ThreadLocal Variables,En particulier dans le scénario threadpool,Les fils sont souvent réutilisés,Si vous ne nettoyez pas Personnalisé ThreadLocal Variables,Peut affecter la logique opérationnelle ultérieure et causer des problèmes tels que des fuites de mémoire.Essayez de l'utiliser dans un agent try-finally Bloc pour recyclage. */
public class ThreadLocalDemo2
{
public static void main(String[] args) throws InterruptedException
{
MyData myData = new MyData();
ExecutorService threadPool = Executors.newFixedThreadPool(3);
try
{
for (int i = 0; i < 10; i++) {
threadPool.submit(() -> {
try {
Integer beforeInt = myData.threadLocalField.get();
myData.add();
Integer afterInt = myData.threadLocalField.get();
System.out.println(Thread.currentThread().getName()+"\t"+"beforeInt:"+beforeInt+"\t afterInt: "+afterInt);
} finally {
myData.threadLocalField.remove(); // Ne pas nettoyer à temps , Peut entraîner des résultats de calcul incorrects
}
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
3.Thread,ThreadLocal,ThreadLocalMap Relations
ThreadEtThreadLocal
Les fils respectifs ,Une personne.
ThreadLocalEtThreadLocalMap
Résumé général des trois
threadLocalMapEn fait, c'est unthreadLocalExemple:key,Tout objet estvalueDeEntryObjet(k,vPaire de clés).
Quand nous sommesthreadLocalAffectation des variables,En fait, à l'heure actuellethreadLocalExemple:key,La valeur est:valueDeEntryPar làthreadLocalMapStockage intermédiaire
Une approximation peut être interprétée comme:ThreadLocalMapC'est littéralement une préservationThreadLocalObjetmap(En fait,ThreadLocalPourKey),Mais après deux couches d'emballageThreadLocalObjet
JVMMaintenance interne d'une version threadéeMap<Thread,T>(AdoptionThreadLocalObjetsetMéthodes,Il s'avère queThreadLocalL'objet lui - mêmekey,C'est parti.ThreadLoalMapMoyenne),Chaque fil utilise ceciTQuand,Utilisez le fil courant pourMapÀ l'intérieur.,De cette façon, chaque thread a sa propre variable indépendante,Une personne.,Les conditions de concurrence ont été complètement éliminées,Variable absolument sûre en mode simultané.
4.ThreadLocalProblème de fuite de mémoire
1.Qu'est - ce qu'une fuite de mémoire
La mémoire utilisée par l'objet ou la variable qui n'est plus utilisée ne peut pas être récupérée,C'est une fuite de mémoire..
2.ThreadLocalMapAvecWeakReference
ThreadLocalMap C'est littéralement une préservationThreadLocalObjetmap(En fait, c'est comme ça queKey),Mais après deux couches d'emballageThreadLocalObjet: (1)La première couche d'emballage est utilisée WeakReference<ThreadLocal <?>> Oui. ThreadLocal L'objet devient un objet faiblement référencé ; (2)La deuxième couche d'emballage définit une classe spécifique Entry Pour étendre WeakReference <ThreadLocal <?>>
3.Forte citation、Référence souple、Faible référence、Qu'est - ce que les références virtuelles, respectivement?
À propos du schéma général référencé
En généralnewObjet,C'estReference(Forte citation)
Il y a trois sous - catégories de citations fortes :SoftReference(Doux.),WeakReference(Faible),PhantomReference(Vide)
java Utilisation autorisée de la technologie finalize() Méthode faire le nettoyage nécessaire avant que le collecteur d'ordures nettoie l'objet de la mémoire.
1.Forte citation(Mode de support par défaut)
Quand il n'y a pas assez de mémoire,JVMCommencer la collecte des ordures,Pour les objets fortement référencés,Même si c'est arrivé.OOMEt l'objet ne sera pas recyclé,La mort ne prend pas.
Les références fortes sont nos références d'objets ordinaires les plus courantes,Tant qu'il y a une forte référence à un objet,Pour montrer que l'objet“Vivant.”,Le collecteur d'ordures ne touche pas ce genre d'objet.In Java La citation la plus courante est forte,Assigner un objet à une variable de référence,Cette variable de référence est une référence forte.Lorsqu'un objet est référencé par une variable fortement référencée,Il est accessible,Il ne peut pas être recyclé par un mécanisme de collecte des ordures,Même si cet objet ne sera jamais utilisé,JVMNi recyclé..Par conséquent, une forte référence estJavaUne des principales causes des fuites de mémoire.
Pour un objet normal,S'il n'y a pas d'autres relations de référence,Tant que le champ d'application de la référence est dépassé ou explicitement en conséquence (Fort.)La référence est assignée à null,On pense généralement qu'ils peuvent être ramassés par les ordures(Bien sûr, le moment exact du recyclage dépend de la stratégie de collecte des ordures).
class MyObject
{
// Cette méthode n'a généralement pas besoin d'être réécrite , C'est juste pour vous donner un exemple.
@Override
protected void finalize() throws Throwable
{
// finalize Le but général d'un objet est d'effectuer une opération de nettoyage avant qu'il ne soit irrémédiablement jeté .
System.out.println("-------invoke finalize method~!!!");
}
}
public static void strongReference()
{
MyObject myObject = new MyObject();
System.out.println("-----gc before: "+myObject);
myObject = null;
System.gc();
try {
TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); }
System.out.println("-----gc after: "+myObject);
}
2.Référence souple
Les références douces sont des références relativement fortes qui affaiblissent certaines références,J'en ai besoin.java.lang.ref.SoftReferenceClasse pour implémenter,Les objets peuvent être exemptés de la collecte des ordures.
Pour les objets qui n'ont que des références douces,
Quand le système a assez de mémoire Ça ne va pas Recyclé,
Lorsque le système est hors de mémoire Oui. Recyclé.
Les références douces sont généralement utilisées dans les programmes sensibles à la mémoire,Par exemple, la mise en cache est utile pour les références douces,Gardez - le quand il y a assez de mémoire.,Recycler si ce n'est pas suffisant!
private static void softReference()
{
SoftReference<MyObject> softReference = new SoftReference<>(new MyObject());
//System.out.println("-----softReference:"+softReference.get());
System.gc();
try {
TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); }
System.out.println("-----gc afterAssez de mémoire: "+softReference.get());// La sortie a une valeur
try
{
byte[] bytes = new byte[20 * 1024 * 1024];//ParamètresJVM Mémoire de démarrage inférieure à 20M,Création20MBObjet
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("-----gc afterPas assez de mémoire: "+softReference.get());//Produits:null, Pas assez de mémoire pour récupérer
}
}
3.Faible référence
Les références faibles nécessitentjava.lang.ref.WeakReferenceClasse pour implémenter,Il a une durée de vie plus courte que les références douces,Pour les objets avec des références faibles,Dès que le mécanisme de collecte des ordures fonctionne,Peu importeJVMEst assez d'espace mémoire pour,Récupère la mémoire utilisée par cet objet.
private static void weakReference()
{
WeakReference<MyObject> weakReference = new WeakReference<>(new MyObject());
System.out.println("-----gc before Assez de mémoire: "+weakReference.get());//Ça vaut le coup.
System.gc();
//Pause pendant quelques secondes
try {
TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); }
System.out.println("-----gc after Assez de mémoire: "+weakReference.get());//Produits:null
}
Scénarios applicables aux références douces et faibles
Si une application doit lire beaucoup d'images locales:
Si l'image est lue à partir du disque dur à chaque fois qu'elle est lue, les performances peuvent être gravement affectées,
Si vous chargez tout en mémoire en même temps, il peut causer un débordement de mémoire.
L'utilisation d'une référence souple à ce stade peut résoudre ce problème.
L'idée de conception est:Avec unHashMap Pour enregistrer le chemin de l'image associé à l'objet d'image correspondant Référence soupleRelations cartographiques entre,Quand il n'y a pas assez de mémoire,JVMRécupère automatiquement l'espace occupé par ces objets d'image mis en cache,Pour éviter efficacementOOMLa question de.
Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
Référence souple:Assez de mémoire Non recyclé, Pas assez de mémoire Recyclage
Faible référence:Tant quegcMise en œuvre, Mémoire suffisante ou non ,Sont recyclés
4.Référence virtuelle
1 Les références virtuelles doivent être dans la file d'attente des références (ReferenceQueue)Utilisation conjointe
Les références virtuelles nécessitentjava.lang.ref.PhantomReferenceClasse pour implémenter.Comme son nom l'indique,C'est un faux.,Contrairement à plusieurs autres références,Une référence virtuelle ne détermine pas le cycle de vie d'un objet.Si un objet ne contient que des références virtuelles,C'est comme s'il n'y avait pas de référence.,Peut être recyclé par un collecteur d'ordures à tout moment,Il ne peut pas être utilisé seul ou accéder à des objets par lui,Les références virtuelles doivent être dans la file d'attente des références (ReferenceQueue)Utilisation conjointe.
2 PhantomReferenceDegetLes méthodes reviennent toujoursnull
La fonction principale de la référence virtuelle est de suivre l'état de l'objet recyclé par les ordures.Simplement pour s'assurer que l'objet est finalizePlus tard, Mécanisme de notification pour faire certaines choses .
PhantomReferenceDegetLes méthodes reviennent toujoursnull,Impossible d'accéder à l'objet de référence correspondant.
3 Traitement des notifications de surveillance
En d'autres termes,,Définir le seul but de l'Association de référence virtuelle,C'est quand cet objet est recyclé par le collecteur pour recevoir une notification système ou ajouter un traitement ultérieur.Pour réaliser un rapportfinalizationMécanismes plus souples pour les opérations de recyclage.
Méthode de construction
File d'attente de référence
La file d'attente référencée doit être sauvegardée avant d'être recyclée. .
public static void main(String[] args)
{
//ParamètresjvmParamètres de démarrage,Mémoire10M
MyObject myObject = new MyObject();
ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
PhantomReference<MyObject> phantomReference = new PhantomReference<>(myObject,referenceQueue);
//System.out.println(phantomReference.get()); //PhantomReferenceDegetLes méthodes reviennent toujoursnull
List<byte[]> list = new ArrayList<>();
new Thread(() -> {
while (true){
list.add(new byte[1 * 1024 * 1024]);
try {
TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) {
e.printStackTrace(); }
System.out.println(phantomReference.get()+"\t"+"list add ok");
}
},"t1").start();
new Thread(() -> {
while (true){
Reference<? extends MyObject> reference = referenceQueue.poll();
if(reference != null){
System.out.println("----- Objet virtuel recyclé dans la file d'attente ");
break;
}
}
},"t2").start();
}
5.GCRootsEt quatre petites citations
Relations
ThreadLocal C'est une coquille , La vraie structure de stockage est ThreadLocal- Oui.ThreadLocalMapCatégorie interne,ChaqueThreadL'objet maintient unThreadLocalMapRéférences
ThreadLocalMap- Oui.ThreadLocalClasse interne,Avec**Entry(k,v)**Pour le stockage
1)AppelezThreadLocalDeset()La méthode,En fait, c'est versThreadLocalMapSetPoint,key- Oui.ThreadLocalObjet,ValeurValueC'est l'objet passé
2)AppelezThreadLocalDeget()La méthode,En fait, c'est versThreadLocalMapObtenir la valeur,key- Oui.ThreadLocalObjet
ThreadLocalNe stocke pas les valeurs en soi,C'est juste lui - même en tant quekeyPour que les threadsThreadLocalMapAccèsvalue,C'est ce principe.,Alors...ThreadLocalCapable de réaliser“Isolement des données”,Obtient la valeur de la variable locale pour le thread courant,Non affecté par d'autres fils
6.Pourquoi utiliser des références faibles?Pas besoin de?
public void function01()
{
ThreadLocal tl = new ThreadLocal<Integer>(); //line1
tl.set(2021); //line2
tl.get(); //line3
}
//line1Un nouveauThreadLocalObjet,t1 Est une référence forte à cet objet;
//line2Appelezset()Créer un nouveauEntry,Le code sourceEntryDans l'objetkEst une référence faible pointant vers cet objet
Pourquoi le code source est - il faiblement référencé
Quandfunc1Une fois la méthode terminée,Stack frame destroy strong references tl Il n'y en a plus..Mais à ce stade,ThreadLocalMapIl y en a un.entryDekeyLa référence pointe également vers cet objet.
Si çakeyLa référence estForte citation,Peut conduire àkeyPointéThreadLocalObjet etvL'objet pointé ne peut pas êtregcRecyclage,CauseFuite de mémoire;
Si çakeyLa référence estFaible référence,JusteGrande probabilitéRéduit les problèmes de fuite de mémoire(Il y en a un autre.keyPournullRay.).
Utiliser des références faibles,Pour faireThreadLocalL'objet a été récupéré avec succès après l'exécution de la méthode etEntryDekeyLes références pointent versnull.
Après ça, on appelleget,setOuremoveLa méthode,Je vais essayer de supprimerkeyPournullDeentry,Peut être libérévalueMémoire utilisée par l'objet.
1.Est - ce qu'une faible citation va tout arranger?
1)Quand nous sommesthreadLocalAffectation des variables,En fait, c'est le momentEntry(threadLocalExemple:key,La valeur est:value)Par làthreadLocalMapStockage intermédiaire.EntryDanskeyC'est une faible référence,QuandthreadLocalLes références externes fortes sont définies ànull(tl=null),Alors le système GC Quand,Selon l'analyse d'accessibilité,C'estthreadLocalL'Instance n'a pas de lien auquel se référer,C'estThreadLocalIl doit être recyclé,C'est comme ça.,ThreadLocalMapEt ça apparaîtrakeyPournullDeEntry,Il n'y a aucun moyen d'y accéderkeyPournullDeEntryDevalue,Si le thread actuel se termine plus tard( Avec pool de threads ),CeskeyPournullDeEntryDevalueIl y aura toujours une chaîne de référence forte:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> valueJamais recyclable,Cause des fuites de mémoire.
2)Bien sûr.,Si actuellementthreadFin de l'opération,threadLocal,threadLocalMap,EntryPas de chaîne de référence accessible,Les déchets sont recyclés par le système pendant le recyclage.
3) Mais dans la pratique, nous avons parfois Pool de Threads disponibles Pour maintenir nos fils,Comme dansExecutors.newFixedThreadPool()Quand un thread est créé,Pour réutiliser les fils, ça ne se termine pas,Alors...threadLocalLes fuites de mémoire méritent notre attention
2.keyPournullDeentry,Analyse des principes
ThreadLocalMapUtiliserThreadLocalUne faible référence àkey,Si unThreadLocalIl n'y a pas de références externes,Alors le systèmegcQuand,C'estThreadLocalIl doit être recyclé,C'est comme ça.,ThreadLocalMapEt ça apparaîtrakeyPournullDeEntry,Il n'y a aucun moyen d'y accéderkeyPournullDeEntryDevalue,Si le thread actuel se termine plus tard(Par exemple, juste avec le pool de Threads),CeskeyPournullDeEntryDevalueIl y aura toujours une chaîne de référence forte.
Même si les références sont faibles,Promis.keyPointéThreadLocalLes objets peuvent être recyclés à temps,MaisvPointévalueL'objet est deThreadLocalMapAppelezget、setJ'ai trouvékeyPournullPour récupérer toutentry、value,Les références faibles ne peuvent donc pas100%Assurez - vous que la mémoire ne fuit pas.On va utiliser unThreadLocalAprès l'objet,Appel manuelremoevComment le supprimer,Surtout dans le pool de programmes en ligne,Ce n'est pas juste une fuite de mémoire,Parce que les Threads dans le pool de Threads sont réutilisés,Ça veut dire que ce threadThreadLocalMapLes objets sont également réutilisés,Si on n'appelle pas manuellementremoveMéthodes,Il est donc possible que les fils suivants obtiennent ce que le fil précédent a laissévalueValeur,Causebug.
3.set、getLa méthode vérifie que toutes les clés sontnullDeEntryObjet
expungeStaleEntry EffacerThread Déchets Entry
set()
get()
remove()
Conclusions:
De l'avantset,getEntry,removeLa méthode,InthreadLocalDans le cycle de vie,PourthreadLocalProblème de fuite de mémoire, Tout ira bien.expungeStaleEntry,cleanSomeSlots,replaceStaleEntryCes trois façons de nettoyerkeyPournullSale.entry.
4.Conclusions
5.Meilleures pratiques
1) Initialiser autant que possible :ThreadLocal.withInitial(() -> Initialisation)
2)Suggestion:Prends ça.ThreadLocalModifier comme suit:static
3)Obligatoire:N'oubliez pas de le faire manuellementremove()
ThreadLocal Peut réaliser l'isolement des données du fil , Pas lui - même. ,Et c'estThreadDeThreadLocalMap
Alors...,ThreadLocal Peut être initialisé une seule fois , Il suffit d'allouer un espace , Il n'est pas nécessaire d'être initialisé plus d'une fois en tant que variable membre .
5.Résumé
- ThreadLocal Ne résout pas le problème du partage des données entre les fils
- ThreadLocal Pour les scénarios où les variables sont isolées entre les Threads et partagées entre les méthodes
- ThreadLocal Les problèmes de sécurité des Threads d'instance sont évités en créant implicitement des répliques d'instance indépendantes dans différents Threads
- Chaque thread possède une propriété exclusiveMapEt l'entretienThreadLocalCartographie des objets et des instances spécifiques,LeMapParce qu'il n'est accessible que par le thread qui le tient,Il n'y a donc pas de problème de sécurité du fil et de verrouillage
- ThreadLocalMapDeEntryC'est exact.ThreadLocalLa référence à est faible,J'ai évité.ThreadLocalProblème avec les objets qui ne peuvent pas être recyclés
- Tout ira bien.expungeStaleEntry,cleanSomeSlots,replaceStaleEntryLes trois méthodes de récupération des clés sont null De Entry Valeur de l'objet(C'est un exemple concret)Et Entry L'objet lui - même empêche les fuites de mémoire,Méthode de renforcement de la sécurité
边栏推荐
- 【OS命令注入】常见OS命令执行函数以及OS命令注入利用实例以及靶场实验—基于DVWA靶场
- 机械硬盘和ssd固态硬盘的原理对比分析
- Kyndryl与Oracle和Veritas达成合作
- 【PHP代码注入】PHP语言常见可注入函数以及PHP代码注入漏洞的利用实例
- 力扣 第 81 场双周赛
- CMOS level circuit analysis
- Semaphore of thread synchronization
- Principle Comparison and analysis of mechanical hard disk and SSD solid state disk
- 美国芯片再遭重击,继Intel后又一家芯片企业将被中国芯片超越
- 基于 Nebula Graph 构建百亿关系知识图谱实践
猜你喜欢
全球芯片市场或陷入停滞,中国芯片逆势扩张加速提升自给率
American chips are hit hard again, and another chip enterprise after Intel will be overtaken by Chinese chips
AQS抽象队列同步器
EventLoop learning
Debug tool
芯片供给过剩之际,进口最多的中国继续减少进口,美国芯片慌了
Too many requests at once, and the database is in danger
SFINAE
External memory
做一篇人人能搞懂的ThreadLocal(源码)
随机推荐
Principle Comparison and analysis of mechanical hard disk and SSD solid state disk
Too many requests at once, and the database is in danger
The second part of the travel notes of C (Part II) structural thinking: Zen is stable; all four advocate structure
NLP - monocleaner
E-week finance Q1 mobile banking has 650million active users; Layout of financial subsidiaries in emerging fields
[WUSTCTF2020]girlfriend
Redis持久化
[xman2018 qualifying] pass
Synchronized与锁升级
Debug tool
Li Kou's 81st biweekly match
ThreadLocal之强、弱、软、虚引用
The global chip market may stagnate, and China's chip expansion accelerates to improve its self-sufficiency rate against the trend
[advanced mathematics] from normal vector to surface integral of the second kind
Reflection learning summary
Nvidia Deepstream 运行延迟,卡顿,死机处理办法
AcWing 第57 场周赛
[advanced MySQL] MTS master-slave synchronization principle and Practice Guide (7)
PCL库——报错解决:安装时遇到的cmake与anaconda的冲突问题
剑指 Offer II 039. 直方图最大矩形面积 单调栈