In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-05 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "what are the three pits of ThreadLocal". Interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "what are the three pits of ThreadLocal"?
Memory leak
Because the key of ThreadLocal is a weak reference, it will result in a corresponding value memory leak if you do not call remove cleanup after use.
@ Test public void testThreadLocalMemoryLeaks () {ThreadLocal localCache = new ThreadLocal (); List cacheInstance = new ArrayList (10000); localCache.set (cacheInstance); localCache = new ThreadLocal ();}
When the value of localCache is reset, cacheInstance is referenced by value in ThreadLocalMap and cannot be GC, but the reference of its key to ThreadLocal instance is a weak reference. Originally, the instance of ThreadLocal is referenced by both localCache and ThreadLocalMap's key, but when the reference of localCache is reset, the instance of ThreadLocal only has a weak reference of ThreadLocalMap's key. At this time, this instance can be cleaned up during GC.
In fact, students who have seen the ThreadLocal source code will know that ThreadLocal itself has a self-cleaning process for Entity whose key is null, but this process depends on the subsequent continued use of ThreadLocal. If the above code is in a second-kill scenario, there will be an instant traffic peak, and this traffic peak will also hit the cluster memory to a high level (or if you are unlucky, it will directly fill the cluster memory and cause failure). Later, due to the peak traffic has passed, the call to ThreadLocal is also reduced, which will reduce the self-cleaning ability of ThreadLocal, resulting in memory leakage. ThreadLocal's self-cleaning is the icing on the cake. Don't expect him to send carbon in the snow.
Compared with value object disclosure stored in ThreadLocal, ThreadLocal needs to pay more attention to ClassLoader leakage when it is used in web container.
The Tomcat official website provides a summary of memory leaks caused by the use of ThreadLocal in web containers. For more information, please see https://cwiki.apache.org/confluence/display/tomcat/MemoryLeakProtection. Here we give one of the examples.
Students who are familiar with Tomcat know that web applications in Tomcat are implemented by Webapp Classloader, a class loader, and Webapp Classloader is implemented by breaking the parent delegation mechanism, that is, all web applications are loaded by Webapp classloader first, which has the advantage of isolating web applications and dependencies in the same container.
Let's look at specific examples of memory leaks:
Public class MyCounter {private int count = 0; public void increment () {count++;} public int getCount () {return count;} public class MyThreadLocal extends ThreadLocal {} public class LeakingServlet extends HttpServlet {private static MyThreadLocal myThreadLocal = new MyThreadLocal (); protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {MyCounter counter = myThreadLocal.get (); if (counter = = null) {counter = new MyCounter (); myThreadLocal.set (counter) } response.getWriter () .println ("The current thread served this servlet" + counter.getCount () + "times"); counter.increment ();}
There are two very critical points to note in this example:
MyCounter and MyThreadLocal must be placed in the path of the web application to ensure that they are loaded by Webapp Classloader
The ThreadLocal class must be an inherited class from ThreadLocal, such as MyThreadLocal in the example, because ThreadLocal is loaded by Common Classloader and its life cycle is the same as that of the Tomcat container. The inheritance class of ThreadLocal includes the more common NamedThreadLocal, so be careful not to step on the pit.
If the Web application where LeakingServlet is located starts, the MyThreadLocal class will also be loaded by Webapp Classloader. If the web application goes offline and the life cycle of the thread does not end (for example, the thread serving LeakingServlet is a thread in a thread pool), then the instance of myThreadLocal will still be referenced by this thread and cannot be GC. At the beginning, this does not cause much problem, because the objects referenced by myThreadLocal do not occupy too much memory space. The problem is that myThreadLocal indirectly holds references to the webapp classloader that loads the web application (which can be referenced through myThreadLocal.getClass (). GetClassLoader ()), while the webapp classloader that loads the web application has references to all the classes it loads, which causes a Classloader leak, which leaks a lot of memory.
Thread context loss in thread pool
ThreadLocal cannot be passed in a parent-child thread, so the most common practice is to copy the ThreadLocal value from the parent thread to the child thread, so you will often see a code like this:
For (value in valueList) {Future taskResult = threadPool.submit (new BizTask (ContextHolder.get (); / / submit the task and set the copy Context to the child thread results.add (taskResult);} for (result in results) {result.get (); / / blocking waiting for task execution}
The submitted task definition looks like this:
Class BizTask implements Callable {private String session = null; public BizTask (String session) {this.session = session;} @ Override public T call () {try {ContextHolder.set (this.session) / / execute business logic} catch (Exception e) {/ / log error} finally {ContextHolder.remove (); / / clean up the context of ThreadLocal to avoid context interlocking} return null;}} when threads are reused
The corresponding thread context management classes are:
Class ContextHolder {private static ThreadLocal localThreadCache = new ThreadLocal (); public static void set (String cacheValue) {localThreadCache.set (cacheValue);} public static String get () {return localThreadCache.get ();} public static void remove () {localThreadCache.remove ();}}
There's no problem writing this. Let's take a look at the thread pool settings:
ThreadPoolExecutor executorPool = new ThreadPoolExecutor (20,40,30, TimeUnit.SECONDS, new LinkedBlockingQueue (40), new XXXThreadFactory (), ThreadPoolExecutor.CallerRunsPolicy)
The last parameter controls how to handle submitted tasks when the thread pool is full. There are four built-in strategies.
ThreadPoolExecutor.AbortPolicy / / throw an exception directly ThreadPoolExecutor.DiscardPolicy / / discard the current task ThreadPoolExecutor.DiscardOldestPolicy / / discard the task in the work queue header ThreadPoolExecutor.CallerRunsPolicy / / transfer to serial execution
As you can see, when we initialize the thread pool, we specify that if the thread pool is full, the newly submitted task will be executed serially, then there will be a problem with our previous writing, when serial execution is called ContextHolder.remove (); the context of the main thread will also be cleaned up, even if the thread pool continues to work in parallel, the context passed to the child threads is already null, and this problem is difficult to find in the pre-release test.
Thread context loss in parallel flow
If ThreadLocal encounters parallel flow, a lot of interesting things will happen, such as the following code:
Class ParallelProcessor {public void process (List dataList) {/ / check parameters first, omit dataList.parallelStream () .forEach (entry-> {doIt ();});} private void doIt () {String session = ContextHolder.get (); / / do something}}
It is easy to find that this code does not work as expected during offline testing, because the underlying implementation of the parallel flow is also a ForkJoin thread pool, and since it is a thread pool, what ContextHolder.get () may pull out is a null. Let's change the code along this line of thinking:
Class ParallelProcessor {private String session; public ParallelProcessor (String session) {this.session = session;} public void process (List dataList) {/ / check parameters first, omit dataList.parallelStream () .forEach (entry-> {try {ContextHolder.set (session)) / / Business processing doIt ();} catch (Exception e) {/ / log it} finally {ContextHolder.remove ();} private void doIt () {String session = ContextHolder.get () / / do something}}
Can this code work after modification? If you are lucky, you will find that there is something wrong with this change. if you are unlucky, this code will run well offline, and this code will go online smoothly. You will soon find some other weird bug in the system. The reason is that the design of parallel flow is special, and the parent thread may also participate in the scheduling of the parallel streamline pool, so if the above process method is executed by the parent thread, then the context of the parent thread will be cleaned. The context that causes subsequent copies to the child thread is null, which also leads to the problem of losing context. The implementation of parallel flow can
At this point, I believe you have a deeper understanding of "what are the three pits of 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.