A quick look at ThreadLocal

Posted by Vik on Mon, 09 Dec 2019 17:49:33 +0100

Welcome to read and communicate with us. Please leave a message if you have any questions.
There are also open sources on GitHub JavaHouse Welcome to star

1 Introduction

In Java8, ThreadLocal is a generic class.This class can provide thread variables.Each thread has its own variables.What does this mean?Every thread has its own resources, just like in real life, every programmer has his own object, no competition, absolute thread security.So what exactly does ThreadLocal do?

Description of Class 2

* This class provides thread-local variables.  These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable.  {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).

This is the description above the ThreadLocal class.This roughly means providing thread variables, usually decorated with static fields.

3 Create him

There are two ways to create ThreadLocal, one through the original parameterless constructor and the other using the lamaba expression of Java8.

3.1 parameterless constructor

Source code

/**
 * Creates a thread local variable.
 * @see #withInitial(java.util.function.Supplier)
 */
public ThreadLocal() {
}

Use and Initialize

private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue(){
            return 1;
        }
    };

3.2 lamaba expression


    /**
     * Creates a thread local variable. The initial value of the variable is
     * determined by invoking the {@code get} method on the {@code Supplier}.
     *
     * @param <S> the type of the thread local's value
     * @param supplier the supplier to be used to determine the initial value
     * @return a new thread local variable
     * @throws NullPointerException if the specified supplier is null
     * @since 1.8
     */
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

Use and Initialize

private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> 1);

In fact, if IDEA is used, the compiler will also prompt for lamaba.But it's interesting to see the source code inside.

4 getter() method

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

Here you can see Thread.currentThread, which gets the current thread, and the ThreadLocalMap class, which is a hash structure (key-value).The getMap() method takes him through the current thread.Then we get the corresponding value from using this keyword as the key.Of course, if it is empty, it will return the initialized value.

5 setter() method

In which case the default value for initialization will not be returned, the answer is to call the setter() method.Look at the source first

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

This roughly means that the current thread is used as the key and the value to be set is placed in the ThreadLocalMap hash structure.Seeing this, you know why ThreadLocal can provide thread variables.He said that each thread is stored separately, each thread has its own independent resources, there is no resource sharing, so thread security.

6 Memory leak

Each thread variable is placed in a ThreadLocalMap, won't there be any memory problems?I intercept part of the source code

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    
}

You can see the WeakReference class here, and you can see that the ThreadLocalMap class is a weak reference. Normally, after executing a thread, it will be recycled by the virtual machine's garbage collection mechanism.But is that true?If threads persist in a thread pool environment, ThreadLocal becomes a strong reference and cannot be recycled.So there is a memory leak problem.

That's when the remove() method comes out.Literally, you know this method cleans up thread variable resources, and it does.Therefore, at the end of the program, it is best to call the emove() method to prevent memory leaks.

Reference resources

Actual Java High Concurrency Programming
ThreadLocal Source Code Generation

Watch the WeChat Public Number and Read on Mobile

Topics: Python Java github Programming Mobile