How does Spring deal with concurrency issues under Bean multithreading? ----- ThreadLocal

ThreadLocal is born to solve the problem of access conflicts for the same variable, so this is a perfect solution for multithreaded access to spring's default singleton bean. Spring also does use ThreadLocal to deal with the thread safety issues of the same variable concurrency under multiple threads.

1. Introduction to ThreadLocal

Then look at what jdk says: This class provides thread-local variables. These variables are different from ordinary variables. Each thread has its own variable, accessed through the get or set method of ThreadLocal, and has an independently initialized copy of the variable. The ThreadLocal instance is usually a private static field in the class that wants to associate the state with the thread.

2. The realization principle of ThreadLocal

Several methods are provided in the ThreadLocal class:
1.public T get() {}
2.public void set(T value) {}
3.public void remove() {}
4.protected T initialValue(){}

public T get()

    public T get() {
    
    
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的threadLocals变量
        ThreadLocalMap map = getMap(t);
        //如果threadLocals变量不为null,就可以在map中查找到本地变量的值
        if (map != null) {
    
    
            //根据ThreadLocal的弱引用的key 查询ThreadLocalMap的Value值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
    
    
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //如果threadLocals为null,则初始化当前线程的threadLocals变量
        return setInitialValue();
    }

    private T setInitialValue() {
    
    
        //此处是返回一个null
        T value = initialValue();
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的threadLocals变量
        ThreadLocalMap map = getMap(t);
        //如果threadLocals不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值
        if (map != null)
            map.set(this, value);
            //如果threadLocals为null,说明首次添加,需要首先创建出对应的map
        else
            createMap(t, value);
        return value;
    }
    void createMap(Thread t, T firstValue) {
    
    
        t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
    }

public void set(T value)

//话不多说,这个容易理解
   public void set(T value) {
    
    
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = getMap(t);
        if (map != null)
        //这个是把ThreadLocal当做key,添加的属性值为value
            map.set(this, value);
        else
            createMap(t, value);
    }

public void remove()

After using ThreadLocal to actively call this method to prevent memory leaks

  public void remove() {
    
    
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
         //当前线程根据ThrealLocal为key值删除
             m.remove(this);
     }

protected T initialValue()

You must override this method to set the default value when you initialize ThreadLocal

protected T initialValue() {
    
    
        return null;
    }

Use of ThreadLocal

1. Let's take a look at the example given by jdk:

public class ThreadId {
    
    
    private static final AtomicInteger nextId = new AtomicInteger(0);
    private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
    
    
        @Override
        protected Integer initialValue() {
    
    
            return nextId.getAndIncrement();
        }
    };

    public static int get() {
    
    
        return threadId.get();
    }
}
class TestID{
    
    
    public static void main(String[] args) {
    
    
        int i = ThreadId.get();
        System.out.println(i);//0
    }
}

If you execute this case, you can see the result of the operation: it is 0, because the initialValue() is overridden above. If this method is not overridden, the return result is null.

2. Verify the isolation of threads:

public class ThreadLocalDemo2 {
    
    
    public static ThreadLocal t1 = new ThreadLocal();
}

class ThreadA extends Thread {
    
    
    @Override
    public void run() {
    
    
        try {
    
    
            for (int i = 0; i < 100; i++) {
    
    
                ThreadLocalDemo2.t1.set("TreadA " + (i + 1));
                System.out.println("Thread get Value = " + ThreadLocalDemo2.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}
class ThreadB extends Thread {
    
    
    @Override
    public void run() {
    
    
        try {
    
    
            for (int i = 0; i < 100; i++) {
    
    
                ThreadLocalDemo2.t1.set("TreadB " + (i + 1));
                System.out.println("Thread get Value = " + ThreadLocalDemo2.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}
class Test {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            ThreadA a = new ThreadA();
            ThreadB b = new ThreadB();
            a.start();
            b.start();
            for (int i = 0; i < 100; i++) {
    
    
                ThreadLocalDemo2.t1.set("Main " + (i + 1));
                System.out.println("Main get Value " + ThreadLocalDemo2.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

Execute this case to see the results: ThreadA thread, ThreadB thread, and Main thread each get the value stored by their own thread. But the call is the same ThreadLocal object, so it is concluded that ThreadLocal can achieve isolation between threads.

3. The non-inheritability of ThreadLocal:

It can be seen from the above example that the data between the main thread and the child threads are isolated and cannot be inherited. Then the subclass of ThreadLocal InheritableThreadLocal provides a solution to this problem.
Let's find out through a case:

public class ThreadLocalDemo6 {
    
    
    public static InheritableThreadLocal<Long> inheritableThreadLocal = new InheritableThreadLocal() {
    
    
        @Override
        protected Long initialValue() {
    
    
            return new Date().getTime();
        }
    };
}
class ThreadA6 extends Thread {
    
    
    @Override
    public void run() {
    
    
        try {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println("在ThreadA 线程中取值 : " + ThreadLocalDemo6.inheritableThreadLocal.get());
                Thread.sleep(100);
            }
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}
class Test6 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println("在线程main 中取值为: " + ThreadLocalDemo6.inheritableThreadLocal.get());
        }
        Thread.sleep(5000);
        ThreadA6 threadA6 = new ThreadA6();
        threadA6.start();
    }
}

Execute this case to see the running result: the attribute value set by the main thread can be obtained in the child thread, then it can be concluded that InheritableThreadLocal has inherited characteristics.

From ThreadLocalMap to see the memory leak problem of improper use of ThreadLocal

Analyze the internal implementation of ThreadLocalMap

The inside of ThreadLocalMap is an Entry array, let’s take a look at the source code of entry

    /**
     * Entry是继承自WeakReference的一个类,该类中实际存放的key是
     * 指向ThreadLocal的弱引用和与之对应的value值(该value值
     * 就是通过ThreadLocal的set方法传递过来的值)
     */
    static class Entry extends WeakReference<ThreadLocal<?>> {
    
    
        /** value就是和ThreadLocal绑定的 */
        Object value;

        //k:ThreadLocal的引用,被传递给WeakReference的构造方法
        Entry(ThreadLocal<?> k, Object v) {
    
    
            super(k);
            value = v;
        }
    }

It can be seen from the above that ThreadLocal is a weak reference, then this reference cannot resist a GC, the key can be recycled, then the value cannot be recycled, which may cause a memory leak (because at this time ThreadLocalMap will have a key null but a value not null Entry item).

Standard use of ThreadLocal

After using ThreadLocal(set() or get()), if it is not in use, use the remove() method to clean up resources in time to avoid memory leaks.

Guess you like

Origin blog.csdn.net/qq_43565087/article/details/106613341