Welcome to pay attention to github.com/hsfxuebao, I hope to help you, if you think it can trouble to click on the Star ha

Big factory interview questions

  • ThreadLocalData structures and relationships for ThreadLocalMap in?
  • ThreadLocalKey is a weak reference, why is this?
  • ThreadLocalDo you know about the memory leak problem?
  • ThreadLocalWhy add the remove method at the end of the?

1. Basic introduction

ThreadLocal provides thread-local variables. These variables are different from normal variables because each thread has its own, independently initialized copy of the variable when it accesses the ThreadLocal instance (through its GET or set methods). ThreadLocal instances are typically private static fields in a class that you want to associate state (for example, user ID or transaction ID) with a thread.

ThreadLocal allows each thread to have its own copy of a local variable. The main solution is to bind each thread to its own value by using the get() and set() methods. Get the default value or change its value to the value of the copy stored in the current thread to avoid thread-safety issues.

The API is introduced:

Code case: according to the total sales statistics, convenient group company to do planning statistics, “the males fight against the deer”, code demonstration is as follows:


class MovieTicket {
    int number = 50;

    public synchronized void saleTicket(a) {
        if(number > 0) {
            System.out.println(Thread.currentThread().getName()+"\t"+"Ticket seller No. :"+(number--));
        }else{
            System.out.println("-------- sold out"); }}}/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
public class ThreadLocalDemo {
    public static void main(String[] args) {
        MovieTicket movieTicket = new MovieTicket();

        for (int i = 1; i <=3; i++) {
            new Thread(() -> {
                for (int j = 0; j <20; j++) {
                    movieTicket.saleTicket();
                    try { TimeUnit.MILLISECONDS.sleep(10); } catch(InterruptedException e) { e.printStackTrace(); } } },String.valueOf(i)).start(); }}}Copy the code

The above demand changes, do not participate in the total calculation, hope to eat their own cooking, each with sales ability commission, according to the singular statistics, such as some housing software, each intermediary sales have their own sales indicators, their own exclusive own, with others. That is, “everyone a world security”, the code is shown as follows:

class House { ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0); public void saleHouse() { Integer value = threadLocal.get(); value++; threadLocal.set(value); }} /** * 1 Three ticket sellers sell 50 tickets, the total amount is completed, eat big pot rice, ticket sellers fixed monthly salary ** 2 cooking stove meals, each salesman do their own work, Public class ThreadLocalDemo {public static void main(String[] args) {House House = new House(); new Thread(() -> { try { for (int i = 1; i <=3; i++) { house.saleHouse(); } System.out.println(Thread.currentThread().getName()+"\t"+"---"+house.threadLocal.get()); }finally { house.threadLocal.remove(); }},"t1").start();}},"t1").start(); new Thread(() -> { try { for (int i = 1; i <=2; i++) { house.saleHouse(); } System.out.println(Thread.currentThread().getName()+"\t"+"---"+house.threadLocal.get()); }finally { house.threadLocal.remove(); } },"t2").start(); new Thread(() -> { try { for (int i = 1; i <=5; i++) { house.saleHouse(); } System.out.println(Thread.currentThread().getName()+"\t"+"---"+house.threadLocal.get()); }finally { house.threadLocal.remove(); } },"t3").start(); System.out.println(Thread.currentThread().getName()+"\t"+"---"+house.threadLocal.get()); }}Copy the code

From the above code, it can be concluded that since each Thread has its own instance copy and this copy is only used by the current Thread, since other threads are not accessible, there is no problem of multi-thread sharing. The initial value is set uniformly, but changes to this value by each thread are independent of each other.

In a word, how not to fight?

  • Add synchronized or Lock to control the access sequence of resources
  • Everyone gets one. Everyone gets it. There’s no need to rob

2. Ali ThreadLocal specification

Ali ThreadLocal specification is as follows:

SimpleDateFormat is a non-thread-safe format.

The date format in SimpleDateFormat is not synchronous. It is recommended to create a separate instance of the format for each thread. If multiple threads access a format at the same time, it must maintain external synchronization.

Write time tool class, generally written as a static member variable, I do not know, this writing method under the risk of multithreading!

The wrong code is shown below:

public class DateUtils {
    public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /** * Simulates a concurrent environment using SimpleDateFormat's parse method to convert a string to a Date object *@param stringDate
     * @return
     * @throws Exception
     */
    public static Date parseDate(String stringDate)throws Exception {
        return sdf.parse(stringDate);
    }
    
    public static void main(String[] args) throws Exception {
        for (int i = 1; i <=30; i++) {
            new Thread(() -> {
                try {
                    System.out.println(DateUtils.parseDate("The 2020-11-11 11:11:11"));
                } catch(Exception e) { e.printStackTrace(); } },String.valueOf(i)).start(); }}}Copy the code

The above code will have the following error:

The SimpleDateFormat class has a Calendar object reference inside it that stores date information related to the SimpleDateFormat, such as sdF.parse (dateStr), sdF.format (date). Methods like this pass in date-related strings, dates, and so on, which are handed over to Calendar references to store. This can cause a problem if your SimpleDateFormat is static, multiple threads will share the SimpleDateFormat as well as the Calendar reference.

Solution 1: Define SimpleDateFormat as a local variable.

Disadvantages: A SimpleDateFormat object is created every time the method is called, and the end of the method is garbage collected.

public class DateUtils {
    /** * Simulates a concurrent environment using SimpleDateFormat's parse method to convert a string to a Date object *@param stringDate
     * @return
     * @throws Exception
     */
    public static Date parseDate(String stringDate)throws Exception {
        return sdf.parse(stringDate);
    }

    public static void main(String[] args) throws Exception {
        for (int i = 1; i <=30; i++) {
            new Thread(() -> {
                try {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    System.out.println(sdf.parse("The 2020-11-11 11:11:11"));
                    sdf = null;
                } catch(Exception e) { e.printStackTrace(); } },String.valueOf(i)).start(); }}}Copy the code

ThreadLocal (also called thread-local variable or thread-local storage)

public class DateUtils {
    private static final ThreadLocal<SimpleDateFormat>  sdf_threadLocal =
            ThreadLocal.withInitial(()-> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    /** * ThreadLocal ensures that each thread gets its own SimpleDateFormat object, so there is no race problem. *@param stringDate
     * @return
     * @throws Exception
     */
    public static Date parseDateTL(String stringDate)throws Exception {
        return sdf_threadLocal.get().parse(stringDate);
    }

    public static void main(String[] args) throws Exception {
        for (int i = 1; i <=30; i++) {
            new Thread(() -> {
                try {
                    System.out.println(DateUtils.parseDateTL("The 2020-11-11 11:11:11"));
                } catch(Exception e) { e.printStackTrace(); } },String.valueOf(i)).start(); }}}Copy the code

3. ThreadLocal source code analysis

The relationship between Thread and ThreadLocal is as follows:

ThreadLocal and ThreadLocalMapThread, ThreadLocal, ThreadLocalMap, and All are summarized as follows:

A threadLocalMap is essentially an Entry object with a threadLocal instance as a key and any object as a value. When we assign a value to a threadLocal variable, we actually store an Entry of value into the threadLocalMap with the current threadLocal instance as the key.

Source code screenshot is as follows:

ThreadLocal get() ¶

GetMap (t) getMap(t)

The Entry class is an inner class of the ThreadLocalMap class, as follows:

A ThreadLocalMap is a two-layer wrapper that holds a ThreadLocal object (with a ThreadLocal Key) :

The JVM maintains a threaded version of the Map (using the set method of the ThreadLocal object, the result is that the ThreadLocal object itself is used as a key in the ThreadLoalMap). Use the current thread to fetch from the Map, so that each thread has its own independent variable, everyone has a copy, the race condition is completely eliminated, in concurrent mode is absolutely safe variable. ,t>

4. The ThreadLocal memory leaks

4.1 Memory Leaks

Memory leak: Memory that is no longer used by objects or variables cannot be reclaimed.

4.2 Strong Reference, Soft Reference, Weak Reference, and Virtual Reference

A ThreadLocalMap is literally a map that holds a ThreadLocal object (with its Key), but a ThreadLocal object wrapped in two layers:

  • (1) The first layer of packaging is usedWeakReference<ThreadLocal> Change the ThreadLocal object into oneA weak referenceThe object;
  • (2) The second layer of packaging is to define a special class Entry to expand WeakReference> :

The overall architecture of references is as follows:

Java technology allows you to use the Finalize () method to do the necessary cleanup before the garbage collector purifies objects from memory.

4.2.1 Strong Reference (Default mode)

When the JVM runs out of memory, it starts garbage collection. For strongly referenced objects, it does not collect them, even if they are in OOM.

Strong references are our most common plain object references, and as long as there is a strong reference to an object, it indicates that the object is “alive” and won’t be touched by the garbage collector. The most common in Java is strong references, where an object is assigned to a reference variable that is a strong reference. When an object is referenced by a strongly referenced variable, it is in a reachable state and cannot be collected by the garbage collection mechanism, even if the object will never be used or collected by the JVM. So strong references are one of the main causes of Java memory leaks.

An ordinary object that has no other reference relationship is generally considered ready for garbage collection if it exceeds the scope of the reference or explicitly sets the corresponding (strong) reference to NULL (depending on the garbage collection policy).

The code for strong references is shown below:

class MyObject {
    // This method is usually not used in the work, here to explain gc, for students to demonstrate
    @Override
    protected void finalize(a) throws Throwable
    {
        System.out.println("------------- gc ,finalize() invoked"); }}public class ReferenceDemo {
/** * strong reference code demo */
    public static void strongReference(a) {
        MyObject myObject = new MyObject();// Default, strong reference, dead do not let go
        System.out.println("gc before: "+myObject);

        myObject = null;
        System.gc();// Enable Gc collection in manual mode.
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

        System.out.println("gc after: "+myObject); }}Copy the code

4.2.2 Soft references

Soft references is a relatively strong reference to weaken some of reference, need to use Java. Lang. Ref. SoftReference class to implement, can let the object save some garbage collection.

For objects that only have soft references,

  • It is not recycled when the system is full of memory,
  • When the systemIt is reclaimed when memory runs out.

Soft references are usually used in memory-sensitive programs, such as caches. When memory is enough, they are kept and recycled! The code is shown as follows:

public static void softReference() { SoftReference<MyObject> softReference = new SoftReference<>(new MyObject()); System.out.println(" GC before GC is sufficient: "+softReference); System.gc(); // Enable Gc collection in manual mode. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } system.out. println("gc after enough memory: "+softReference); */ // Set the -xms10m -XMx10m system.out. println(" GC before: "+softReference); try { byte[] bytes = new byte[9 * 1024 * 1024]; }catch (Exception e){e.printStackTrace();}catch (Exception e){e.printStackTrace(); }finally {system.out.println ("-----gc after out of memory: "+softReference.get()); }}Copy the code

holdingsA weak reference

Weak references need to use Java. Lang. Ref. WeakReference class to do this, it is more shorter than soft references of survival, for only a weak reference object, as long as the garbage collection mechanism run, regardless of whether or not enough of the JVM memory space, can recycle the memory object.

The code is shown as follows:

public static void weakReference() { WeakReference<MyObject> weakReference = new WeakReference(new MyObject()); System.out.println("gc before: "+weakReference.get()); System.gc(); // Enable Gc collection in manual mode. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("gc after: "+weakReference.get()); }Copy the code

Soft and weak references are applicable to the following scenarios: If an application needs to read a large number of local images:

  • If every time an image is read, it is read from the hard disk, then performance is severely affected.
  • If you load all of them into the memory at one time, memory overflow may occur.

Using soft references in this case can solve the problem.

The idea is to use a HashMap to store the mapping between the path of the image and the soft references associated with the corresponding image object. When memory runs out, the JVM automatically reclaims the space occupied by these cached image objects, effectively avoiding OOM problems.

Map<String, SoftReference> imageCache = new HashMap<String, SoftReference>();
Copy the code

4.2.4 Phantom reference

Phantom reference to Java. Lang. Ref. PhantomReference class to implement.

As the name implies, virtual references do not determine the life cycle of an object, unlike the other references. If an object holds only virtual references, it can be collected by the garbage collector at any time, just as if there were no references at all.

It cannot be used alone or through which objects can be accessed, and virtual references must be used in conjunction with reference queues.

The main purpose of a virtual reference is to track the status of an object being garbage collected. It just provides a mechanism to make sure that objects do something when they are finalize. PhantomReference’s get method always returns NULL, so the corresponding reference object cannot be accessed.

This means that an object has entered the finalization stage and can be collected by gc to implement a more flexible collection operation than finalization mechanisms.

In other words, the only purpose of setting up a virtual reference association is to receive a system notification or to add further processing later when the object is reclaimed by the collector.

The construction method is as follows:The reference queue looks like this: I need to be stored in the reference queue before I can be reclaimed.

The code is shown as follows:

Class MyObject {// This method is not used in the work. Throw Throwable {system.out.println ("------------- gc,finalize()) invoked"); } } public class ReferenceDemo { public static void main(String[] args) { ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>(); PhantomReference<MyObject> phantomReference = new PhantomReference<>(new MyObject(),referenceQueue); System.out.println(phantomReference.get()); 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()); } },"t1").start(); new Thread(() -> { while(true) { Reference<? extends MyObject> poll = referenceQueue.poll(); if (poll ! = null) {system.out.println ("------ a virtual object entered the queue "); }}},"t2").start(); // Pause the thread for a few SECONDS try {timeunit.seconds.sleep (5); } catch (InterruptedException e) { e.printStackTrace(); } } public static void weakReference() { WeakReference<MyObject> weakReference = new WeakReference(new MyObject()); System.out.println("gc before: "+weakReference.get()); System.gc(); // Enable Gc collection in manual mode. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("gc after: "+weakReference.get()); } public static void softReference() { SoftReference<MyObject> softReference = new SoftReference<>(new MyObject()); System.out.println(" GC before GC is sufficient: "+softReference); System.gc(); // Enable Gc collection in manual mode. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } system.out. println("gc after enough memory: "+softReference); */ // Set the -xms10m -XMx10m system.out. println(" GC before: "+softReference); try { byte[] bytes = new byte[9 * 1024 * 1024]; }catch (Exception e){e.printStackTrace();}catch (Exception e){e.printStackTrace(); }finally {system.out.println ("-----gc after out of memory: "+softReference.get()); Public static void strongReference() {MyObject MyObject = new MyObject(); System.out.println("gc before: "+myObject); myObject = null; System.gc(); // Enable Gc collection in manual mode. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("gc after: "+myObject); }}Copy the code

4.2.5 Summary of GCRoots and four Major citations

4.3 Relationship between ThreadLocal and four References

Each Thread object maintains a reference to a ThreadLocalMap

ThreadLocalMap is an internal class of ThreadLocal that uses Entry for storage,

When we call ThreadLocal’s set() method, we actually set a Value to the ThreadLocalMap, where the key is the ThreadLocal object and the Value is the object passed in

When you call ThreadLocal’s get() method, you actually fetch the value from ThreadLocalMap. The key is a ThreadLocal object.

ThreadLocal does not store values itself, but rather acts as a key that allows a thread to fetch values from a ThreadLocalMap. This allows a ThreadLocal to achieve “data isolation” by fetching values that are local to the current thread without being affected by other threads

4.3 Why weak References? How about not?

public void function01() {
    ThreadLocal tl = new ThreadLocal<Integer>();    //line1
    tl.set(2021);                                   //line2
    tl.get();                                       //line3
}

Copy the code

Line1 creates a new ThreadLocal object. T1 has a strong reference to this object. Line2 calls set() and creates an Entry. K in the Entry object is a weak reference to this object.

4.3.1 Why Weak References?

When the function01 method completes, the stack frame destruction strong reference TL is gone. But the key reference to an entry in the thread’s ThreadLocalMap still points to the object

If the key reference is a strong reference, then the ThreadLocal object that the key points to and the object that v points to cannot be collected by gc, resulting in a memory leak.

If the key reference is a weak reference, the memory leak problem (and a null key thunder) will be greatly reduced. With weak references, ThreadLocal objects can be successfully reclaimed after method execution with Entry’s key reference pointing to NULL.

4.4 Is weak reference all right?

  • When we assign a value to a threadLocal variable, we actually store the current Entry(threadLocal instance key, value value) into the threadLocalMap. The key in the Entry is a weak reference. If the external strong reference of threadLocal is set to NULL (TL = NULL), then no link of the threadLocal instance can reference it during system GC according to the reachable analysis, and the threadLocal is bound to be reclaimed. There is no way to access the values of these null-key entries. If the current thread does not terminate, the values of these null-key entries will remain in a strong reference chain: Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value can never be reclaimed, causing a memory leak.

  • Of course, if threadLocal, threadLocalMap, and Entry are not reachable, they will all be collected by the system during garbage collection.

  • But in actual use, we sometimes use the thread pool to maintain our thread, such as Executors. NewFixedThreadPool () is created when the thread, in order to reuse is not the end of the thread, so the threadLocal memory leak is worth our careful

4.4.1 How to parse an entry with a NULL key

As follows:

A ThreadLocalMap uses a weak reference to a ThreadLocal as its key. If a ThreadLocal has no external strong reference to it, then the ThreadLocal must be reclaimed during gc. There is no way to access the values of these null-key entries. If the current thread does not terminate (for example, if it happens to be in a thread pool), The value of these entries with a null key will always have a strong reference chain.

Although weak reference ensures that the ThreadLocal object pointed to by key can be recycled in time, the value object pointed to by V will recycle the entire entry and value only when the key is found to be null when ThreadLocalMap calls GET and set. Therefore, weak references are not a 100% guarantee against memory leaks. When a ThreadLocal object is no longer in use, you need to manually remove it by calling remoev, especially in the thread pool. This is not just a memory leak problem, because threads in the thread pool are reused, which means that the ThreadLocalMap object of that thread is reused. If we do not manually call the remove method, then subsequent threads may get the value left over from the previous thread, causing a bug.

4.4.2 Set and GET methods will check all Entry objects with null keys.

  • Set () method:

  • The get () method

  • Remove () method

    ExpungeStaleEntry is used to remove memory leaks from a threadLocal during its lifetime. CleanSomeSlots replaceStaleEntry, these three methods to remove dirty entry key is null.

4.5 the conclusion

5. Small summary

  • ThreadLocal does not solve the problem of sharing data between threads
  • ThreadLocal is suitable for scenarios where variables are isolated between threads and shared between methods
  • ThreadLocal avoids instance thread-safety problems by implicitly creating separate instance copies in different threads
  • Each thread holds its own Map and maintains the mapping between ThreadLocal objects and specific instances. The Map is accessible only by the thread that holds it, so there are no thread-safety or locking issues
  • The Entry of a ThreadLocalMap refers to a ThreadLocal as a weak reference, avoiding the problem that ThreadLocal objects cannot be reclaimed
  • By expungeStaleEntry cleanSomeSlots, these three methods recycling replaceStaleEntry key Entry of null values of the objects (that is, for example) as well as the Entry object itself to prevent memory leaks, Methods of security hardening,
  • Every man is safe in the world

The resources

Java concurrent programming knowledge system Java concurrent programming art Java multi-threaded programming core technology Java concurrent implementation principle JDK source analysis