ThreadLocal is a thread variable utility class. Is mainly used for private thread and that thread will deposit A copy of the object to do A map, each thread mutual interference between variables, such as A thread and thread B all want to use A variable, at this time there is A strong resources of the problem, and the variable he could have more, managers can use ThreadLocal at this time as A variable, there is no multithreading pitfalls. It also implicitly serves as a tool for passing information within a thread.

ThreadLocal addresses concurrent access to threads from another perspective. ThreadLocal provides a separate copy of variables for each thread, isolating conflicting data access from multiple threads. For example, if you have multiple threads that all use the same object, but you don’t want to interfere with each other, you need ThreadLocal.

To summarize, synchronization takes a time-for-space approach to the problem of multi-threaded resource sharing, while ThreadLocal takes a space-for-time approach. The former provides only one copy of a variable, which different threads queue to access, while the latter provides one copy of a variable for each thread, so it can be accessed simultaneously without affecting each other.

1. To develop

We can see that each thread has a map object, which holds local thread objects and replica variables. The map object is then maintained by a ThreadLocal object, which is responsible for adding and maintaining it

Therefore, for different threads, each time they obtain the duplicate value, other threads cannot obtain the duplicate value of the current thread, forming the isolation of the duplicate and not interfering with each other.

ThreadLocal values are associated with the thread, and the map is maintained by the thread

public class Thread implements Runnable {
    /* ThreadLocal values 如 : To this thread. This map is maintained(occupied by.. Maintenance) * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}
Copy the code

2. Common methods

1. Public T get() gets the copy variable value of the current thread.

2. Public void set(T value) Saves the duplicate variable value of the current thread.

3. Public void remove() removes the current future copy variable value.

4. Public static ThreadLocal withInitial(Supplier Supplier

1. The get 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 initialValue method.

Returns the value in the thread-local variable copy of the current thread. If the variable does not have a value for the current thread, it is first initialized to the value returned by calling the initialValue method.

2. Set method

​ 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 initialValue method to set the values of thread-locals.

Sets the current thread copy of this thread-local variable to the specified value. Most subclasses do not need to override this method and simply rely on the initialValue method to set the value of a thread-local variable.

3. The remove method

Removes the current thread’s value for this thread-local variable. If this thread-local variable is subsequently read by the current thread, its value will be reinitialized by invoking its initialValue method, unless its value is set by the current thread in the interim. This may result in multiple invocations of the initialValue method in the current thread.

1. Delete local variables in this thread. If the thread-local variable is subsequently called again, its value is reinitialized by calling its initialValue method, unless its value was set by the current thread during the transition. This can result in multiple calls to the initialValue method in the current thread.

2. If you do not remove the memory, it will cause memory leakage and overflow.

4. WithInitial method

​ Creates a thread local variable. The initial value of the variable is determined by invoking the get method on the Supplier.

Create a thread-local variable and determine its initial value by calling the GET method.

3. Application scenarios

1. Use of thread pools

public class TestThreadLocal2 {

    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    public static Connection getConnection(a){
        Connection connection = threadLocal.get();
        if (null == connection) {
            connection = Datasource.get();
            threadLocal.set(connection);
        }
        return connection;
    }

    
    public static void remove(a){
        threadLocal.remove();
    }



    public static void main(String[] args) throws InterruptedException {

        Executor executors = Executors.newFixedThreadPool(10);
        executors.execute(()->{
            System.out.println(getConnection());
        });
        executors.execute(()->{
            System.out.println(getConnection());
        });

        System.out.println(getConnection());
    }

    private static class Connection{
        String name;
        public Connection(String name) {
            this.name = name; }}private static class Datasource{
        static ArrayBlockingQueue<Connection> connections = new ArrayBlockingQueue<Connection>(5);

        static {
            connections.offer(new Connection("1"));
            connections.offer(new Connection("2"));
            connections.offer(new Connection("3"));
            connections.offer(new Connection("4"));
            connections.offer(new Connection("5"));
        }

        static Connection get(a){
            Connection connection =null;
            try {
                // Remove and return the blocking operation on the header element
                connection = connections.take();
                // Add the removed elements back. For example, we started with 1, 2, 3, 4, 5. Now take becomes 2, 3, 4, 5, and put becomes 2, 3, 4, 5, 1.
                connections.put(connection);
            } catch (InterruptedException e) {
                System.out.println("Thread blocking");
            }
            returnconnection; }}}Copy the code

2. Use of internal variables

public class MainTest {

    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1000);

        / / using ThreadLocal
        Bank bank = new Bank();
        IntStream.range(0.3).forEach(e->{
            pool.execute(()->{
                bank.deposit(200."New employee:"+e); }); }); pool.shutdown(); }}class Bank {
    // Initialize the account balance to 1000
    ThreadLocal<Integer> account = ThreadLocal.withInitial(() -> 1000);

    public void deposit(int money,String name) {
        System.out.println(name + "-- Current account balance:" + account.get());
        account.set(account.get() + money);
        System.out.println(name + "-- in the" + money + "After account balance is:"+ account.get()); account.remove(); }}Copy the code

Output result:

New employee:0-- Current account balance:1000New employee:0Deposit --200The subsequent account balance is:1200New employee:2-- Current account balance:1000New employee:2Deposit --200The subsequent account balance is:1200New employee:1-- Current account balance:1000New employee:1Deposit --200The subsequent account balance is:1200
Copy the code

3. Concurrent problems with date formatting

public class DateUtil {

    private static ThreadLocal<SimpleDateFormat> sdf = ThreadLocal
            .withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    // 
    public static String format(Date date) {
        String msg = null;
        try {
            // Call the get method -> withInitial to initialize
            msg = sdf.get().format(date);
        } finally {
            / / remove after use
            sdf.remove();
        }
        return msg;
    }


    public static String formatJava8(Date date){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }

    public static void main(String[] args) throws Exception {
        System.err.println(format(new Date()));

        System.err.println(formatJava8(newDate())); }}Copy the code

4. Problem: ThreadLocalmap-entry

static class Entry extends WeakReference<ThreadLocal> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal k, Object v) {
        // Only key is a weak reference,value is not, so it is easy to overflow the memory, if you do not manually remove
        super(k); value = v; }... }Copy the code

5. Use attention issues

The problem of ThreadLocalMap

The key of ThreadLocalMap is a weak reference and the Value is a strong reference. This leads to the problem that when a ThreadLocal has no strong reference to an external object, the weak reference Key will be reclaimed during GC, but the Value will not be reclaimed. If the thread that created the ThreadLocal keeps running, then the Value in the Entry object may not be reclaimed. A memory leak occurs. Procedure

If the Key is a weak reference, we need to call the get() and set() methods of ThreadLocal and then call the remove method to remove the reference between the Entry node and the Map. In this way, the entire Entry object becomes unreachable after GC Roots analysis and can be recycled in the next GC.

If you use a ThreadLocal set method and don’t call the remove method explicitly, memory leaks can occur, so it’s important to develop good programming habits. Remember to call the remove method after using ThreadLocal.