In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article focuses on "how to use ThreadLocal". Interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Now let the editor take you to learn how to use ThreadLocal.
Catalogue
Use
Construction method
Static method
Public method
Memory leak
Solution method
Why define ThreadLocal as a static variable
Thoughts on memory leakage of ThreadLocal
Overview
Use the scene sample code
ThreadLocal uses source code
Think about the problem
ThreadLocal interpretation
ThreadLocal can see something by looking at the name, thread local.
Take a look at java's description of him:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread (through ThreadLocal's get or set method) accesses its own independently initialized copy of the variable. ThreadLocal instances are usually private static fields in a class.
In the above paragraph, a key point is that each thread has its own exclusive variable, which will not be affected by other threads.
Use public class ThreadLocalTwo {/ / statically to extend the life cycle. Final immutable private static final ThreadLocal threalLocal = ThreadLocal.withInitial (()-> {return 0;}); public static void main (String [] args) {new Thread (()-> {while (true) {/ / take out int inner = threalLocal.get () / use System.out.println (Thread.currentThread (). GetName () + "+ inner); LockSupport.parkNanos (TimeUnit.SECONDS.toNanos (1)); / / store update values in threalLocal.set (+ + inner);}}," three ") .start () New Thread (()-> {while (true) {/ / extract int inner = threalLocal.get (); / / use System.out.println (Thread.currentThread (). GetName () + "+ inner); LockSupport.parkNanos (TimeUnit.SECONDS.toNanos (1)) / / the update value is stored in threalLocal.set (+ + inner);}}, "four") .start ();}}
Use this I just casually write a demo, there are many kinds of specific logic, as long as you want, there will be many ways to write. Specific look at business needs.
Personal understanding
ThreadLocal is similar to a tool that removes local copies of changes for the current thread. If you look at the source code of Thread, you will find the following code
/ * ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class. * / ThreadLocal.ThreadLocalMap threadLocals = null
This is a field constructed by a static inner class, so let's take a look at the source code of ThreadLocal.ThreadLocalMap.
Static class Entry extends WeakReference > {/ * * The value associated with this ThreadLocal. * / Object value; Entry (ThreadLocal k, Object v) {super (k); value = v;}}
The following requires your knowledge of java memory management relationships, otherwise you will definitely be blinded.
If you can't read my article, java memory management relationship and how memory leaks work.
Because it is a subclass of WeakReference, ThreadLocal, as a reference object, is likely to be cleared by Entry. If there are no other references to ThreadLocal at this time, it will certainly be recycled by GC.
But value is strongly referenced, and Entry is held by Entry [], Entry [] is held by ThreadLocalMap, and ThreadLocalMap is held by threads. As long as the thread doesn't die or you don't call either of the set,remove methods, the object that value points to will never be recycled. Because it does not meet either of the two conditions for GC recycling.
Imagine if there are enough threads in the thread pool and the local copy you pass to the thread takes up a lot of memory. There is no doubt that memory will overflow.
Solution method
Simply calling the remove method will erase the reference to the previous value so that the thread does not hold the reference from the previous value to the object. There will be no memory exposed.
Some readers will have questions, didn't it say that two let go will make value objects can be recycled, why is there no set method on it?
This is because the set method can indeed be a broken reference to the object that value points to, but at the same time it strongly references a memory space to value. Even if the previous object is recycled, a new object is generated.
As for the get method, calling the get method will cut off the reference to the value only after the ThreadLocalMap is GC.
First of all, let's look at the get source code
Public T get () {Thread t = Thread.currentThread (); / / reference to the current thread / / get the ThreadLocalMap of the current thread, if no null ThreadLocalMap map = getMap (t) is returned / / take the if (map! = null) {/ / the item associated with the key if there is no key, then null / / if the entry of ThreadLocalMap clears the reference to the ThreadLocal object, then this will clear the corresponding value reference ThreadLocalMap.Entry e = map.getEntry (this) If (e! = null) {@ SuppressWarnings ("unchecked") T result = (T) e.value; return result;}} / / if the current thread does not set ThreadLocalMap, then return the value return setInitialValue () of initialValue ();}
The above code calls getEntry, which calls another method internally, which clears the corresponding value reference when the ThreadLocal reference is cleared.
Private Entry getEntry (ThreadLocal key) {/ / the capacity of the location table array is 16 int I = key.threadLocalHashCode & (table.length-1); Entry e = table [I]; / / if (e! = null & & e.get () = key) return e after key has not been recycled Else / / this key is reclaimed, and the corresponding value is released from the reference return getEntryAfterMiss (key, I, e);}
We see the last call to getEntryAfterMiss (key, I, e), which is not the final method to erase the value reference, so let's move on
Private Entry getEntryAfterMiss (ThreadLocal key, int I, Entry e) {Entry [] tab = table; int len = tab.length; while (e! = null) {/ / get the reference ThreadLocal k = e.get () of the reference object held by the weak reference object / / ThreadLocal is not recycled if (k = = key) return e; if (k = = null) / / entry clears the reference to ThreadLocal / / clears the entry's value reference expungeStaleEntry (I) through the element entry of the entry [] array Else I = nextIndex (I, len); e = tab [I];} return null;}
Above, we will focus on expungeStaleEntry (I), which is the final reference to the value object that erases entry. Take a look at the source code of expungeStaleEntry (I)
Private int expungeStaleEntry (int staleSlot) {Entry [] tab = table;// get the table reference int len = tab.length;// to get the length of the table, no accident should be 16 / / expunge entry at staleSlot / / the following two lines of code is the key. Tab [staleSlot] .value = null; tab [staleSlot] = null; size--; / / Rehash until we encounter null Entry e; int i; for (I = nextIndex (staleSlot, len); (e = tab [I])! = null I = nextIndex (I, len)) {ThreadLocal k = e.get (); if (k = = null) {e.value = null; tab [I] = null; size-- } else {int h = k.threadLocalHashCode & (len-1); if (h! = I) {tab [I] = null; / / Unlike Knuth 6.4 Algorithm R, we must scan until / / null because multiple entries could have been stale. While (tab [h]! = null) h = nextIndex (h, len); tab [h] = e;} return I;}
The above code is so long that we don't have to take a closer look at it, just pay attention to the following two lines of code
Tab [staleSlot] .value = null;// clears references so that GC can recycle tab [staleSlot] = null;// to clear its own references.
Get the stored entry through entry [staleSlot], and clear the value reference of the entry through entry.
So you can see that get can also have the same effect as remove.
Let's take another look at the source code of remove
Public void remove () {ThreadLocalMap m = getMap (Thread.currentThread ()); if (m! = null) m.remove (this);}
There is nothing to say in the above code, just look at the remove method of ThreadLocalMap
Private void remove (ThreadLocal key) {Entry [] tab = table; int len = tab.length; / / gets the location, because it is stored in accordance with this rule, int I = key.threadLocalHashCode & (len-1); for (Entry e = tab [I]; e! = null E = tab [I = nextIndex (I, len)]) {/ / it is possible that ThreadLocal will be cleared by entry, then value will be referenced by the thread, and if you do not call the set,get method, you can only wait for the thread to destroy. If (e.get () = = key) {/ / call the weak reference method to clear the reference of the reference object e.clear (); / / wipe out the value expungeStaleEntry (I) corresponding to ThreadLocal; return;}
ExpungeStaleEntry erasure is called above.
Set
We focus on this method.
Private void expungeStaleEntries () {Entry [] tab = table; int len = tab.length; for (int j = 0; j
< len; j++) { Entry e = tab[j]; if (e != null && e.get() == null) //擦除 expungeStaleEntry(j); } } 这个呢 循环调用了expungeStaleEntry(j)方法 ,也是擦除了value的对象引用。 为什么要将ThreadLocal 定义成 static 变量 延长生命周期,之所以是static 是因为,ThreadLocal 我们更应该将他看成是 工具。 对ThreadLocal内存泄漏引起的思考概述 最近在对一个项目进行重构,用到了ThreadLocal。 场景如下: 外围系统会调用接口上传数据,在接口中要记录数据的变化Id,在上传数据完后需要集中在一个地方把这些Id以消息形式发送出去。 使用场景样例代码public Result uploadOrder(TotalPayInfoVo totalPayInfoVo) { try { saveTotalPayInfoVo(totalPayInfoVo); //发送消息 UnitWork.getCurrent().pushMessage(); } catch (Exception e) { cashLogger.error("uploadOrder error,data: {}, error: {}", JSON.toJSONString(totalPayInfoVo), e); throw new RuntimeException("保存失败", e); } finally { UnitWork.clean();// } return ResultUtil.successResult();避免内存泄漏 }ThreadLocal使用源码/** * 工作单元,在同一个线程中负责记录一个事件或者一个方法或者一个事务过程中产生的变化,等操作结束后再处理这种变化。 */public class UnitWork { private UnitWork() { } private static ThreadLocal current = new ThreadLocal() { protected UnitWork initialValue() { return new UnitWork(); } }; /** * 状态变化的instance */ private Set statusChangedInstances = new HashSet(); public void addStatusChangedInstance(String instance) { statusChangedInstances.add(instance); } /** * 推送消息 */ public void pushMessage() { for(String id : statusChangedInstances){ //异步发消息 } } public static UnitWork getCurrent() { return current.get(); } /** * 删除当前线程的工作单元,建议放在finally中调用,避免内存泄漏 */ public static void clean() { current.remove(); }}思考问题 为了避免内存泄漏,每次用完做一下clean清理操作。发送消息的过程是异步的,意味着clean的时候可能和发送消息同时进行。那么会不会把这些Id清理掉?那么可能造成消息发送少了。要回答这个问题,首先要搞懂ThreadLocal的引用关系,remove操作做了什么? ThreadLocal解读 ThreadLocal可以分别在各个线程保存变量独立副本。每个线程都有ThreadLocalMap,顾名思义,类似Map容器,不过是用数组Entry[]来模拟的。那么既然类似Map,肯定会存在Key。其实Key是ThreadLocal类型,Key的值是ThreadLocal的HashCode,即通过threadLocalHashCode计算出来的值。 这个Map的Entry并不是ThreadLocal,而是一个带有弱引用的Entry。既然是弱引用,每次GC的时候都会回收。 static class Entry extends WeakReference { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } 而Key对应的value就是要保存在线程副本Object,这里指的就是UnitWork的实例。调用ThreadLocal的get方法时,首先找到当前线程的ThreadLocalMap,然后根据这个ThreadLocal算出来的hashCode找到保存线程副本Object。 他们的关系对应如下:When ThreadLocal is in remove, it calls Entry's clear, that is, the weakly referenced clear method. Remove the reference to Key- > ThreadLocal. The next expungeStaleEntry sets the value reference in entry to null.
/ * Remove the entry for key. * / private void remove (ThreadLocal key) {Entry [] tab = table; int len = tab.length; int I = key.threadLocalHashCode & (len-1); for (Entry e = tab [I]; e! = null E = tab [I = nextIndex (I, len)]) {if (e.get () = = key) {e.clear (); expungeStaleEntry (I); return;}
You can now answer the earlier question. Although both the ThreadLocal and the current thread are out of reference to the Object, the most important point is that the asynchronous thread still has a strong reference path to the Object, that is, a strong reference to the UnitWork instance. Therefore, GC will not reclaim the instance of UnitWork, send messages or have null pointers.
At this point, I believe you have a deeper understanding of "how to use ThreadLocal". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.