Detailed ThreadLocal class

This article contains knowledge

  • What ThreadLocal that?
  • ThreadLocal usage scenarios
  • ThreadLocal achieve the core principles and methods
  • Note the use of ThreadLocal

books What 1.ThreadLocal that?

Thread-safe way to achieve the broad sense include: mutual exclusion synchronization, non-blocking synchronization, without synchronization Scenarios Three, ThreadLocal belongs to a non-synchronous scheme.

ThreadLocal thread local storage is referred to, will be stored as the name suggests shared data to each thread-local copy of the shared data are thus each thread has, in order to limit the shared data visible range or variability. ThreadLocal lock need not be implemented using a thread-safe and efficient than synchronous high mutex, in some cases can be avoided class method parameters passed disadvantages layers. Such as classes RequestContextHolder spring frame (since each request in request thread isolated, not the same information) on the use of ThreadLocal implemented.

books 2.ThreadLocal usage scenarios

  • Each thread needs to exclusive data, or the need to limit the visible range of shared data, such as commonly used tools SimpleDateFormat is not thread-safe, and can be used with ThreadLocal
  • Each thread needs to be saved "global" data, for example, we used the actual project: filters into the reference token to all interfaces on ThreadLocal, reducing layers of token parameter passed to facilitate the call

SimpleDateFormat example below to test the first usage scenario: Each thread exclusive data

public class DateFormatUitls {
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static String format(int milSecond){
        Date date = new Date(milSecond * 50000);
        return sdf.format(date);
    }
}

public class ThreadLocalTest {
    static ExecutorService es = Executors.newFixedThreadPool(10);
    
    public static void main(String[] args) {
        try {
            for (int i = 0; i < 1000; i++) {
                int milSecond = i;
                es.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(DateFormatUitls.format(milSecond));
                    }
                });
            }
        }catch (Exception e){
        }finally{
            es.shutdown();
        }
    }
}

The presence of multi-threaded shared SimpleDateFomat object of concurrency, the output can be seen in the presence of the same point in time, that is, concurrency problems, so we can use ThreadLocal to transform solve (for sdf.format () method is synchronized lock Yes, consider the efficiency):

public class DateFormatUitls {

    public static String format(int milSecond){
        Date date = new Date(milSecond * 1000);
        return ThreadLocalSimpleDateFormat.threadLocal.get().format(date);
    }
}

class ThreadLocalSimpleDateFormat extends ThreadLocal{

   static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
}

public class ThreadLocalTest {
    static ExecutorService es = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        try {
            for (int i = 0; i < 1000; i++) {
                int milSecond = i;
                es.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(DateFormatUitls.format(milSecond));
                    }
                });
            }
        }catch (Exception e){
        }finally{
            es.shutdown();
        }
    }
}

Then the second test common scenarios: save the global data for each thread

I am here to store global project actually uses the token, for example (after a lot of code to streamline):

/**
 * @author simons.fan
 * @description 过滤器进行鉴权和授权
 **/
@Component
@WebFilter(urlPatterns = "/**")
public class SecutityFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //TODO
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String token = servletRequest.getParameter("token");
        //TODO省略参数校验……
        //将token写入ThreadLocal中
        TokenContextHolder.set(StringUtils.isEmpty(token) ? "默认值" : token);
        //TODO省略鉴权……
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        //TODO
    }
}
/**
 * @author simons.fan
 * @description 全局ThreadLocal类型的token容器
 **/
public class TokenContextHolder {

    public static ThreadLocal<String> tokenContext = new ThreadLocal();

    /**
     * 把token放入threadLocal中
     **/
    public static void set(String token) {
        tokenContext.set(token);
    }

    /**
     * 从threadLocal中获取对应线程存储的值
     **/
    public static String get() {
        return tokenContext.get();
    }
}

Follow any interface needs to obtain the token (available through ThreadLocal can directly, without the need to pass parameters layers, and the security thread token is not present, is a token corresponding to each set of threads into the [ Note: link ThreadLocal end position to promptly call the remove () method ] .

books 3.ThreadLocal the principle and core methods

About ThreadLocal principle, we must first get to know , ThreadLocalMap Thread and ThreadLocal relationship between the three. We say Thread object for each thread has a ThreadLocalMap object that stores a set of KEY_VALUE value ThreadLocal.threadLocalHashCode as the key to the value of the variable, ThreadLocal object is the current line of access entry ThreadLocalMap each ThreadLocal object contains a unique threadLocalHashCode value, use this key you can easily and quickly get to the local variables are stored in a ThreadLocal value. So since ThreadLocalMap a map (array structure), will be problematic hash conflict, when the hash conflict actually occurred, ThreadLocalMap uses a linear detection method (continue to search for the next empty position, rather than linked list structure) [Note: this conclusions from the net Mu class curriculum: Fun Java concurrency utilities, proficient JUC, become versatile concurrent  resulting learning ] .

Thread、ThreadLocal、ThreadLocalMap关系图

ThreadLocal core methods

  • initialValue () : used to initialize the thread-local variable value, you need to explicitly rewrite, rewrite words will not return to the default value null, which would delay loading (calling get () method when it will be triggered)
  • set (T var1) : threadLocal manually set value when the set value ThreadLocal explicit, the initialValue not make the call () method initializes. Nature and manner set initialValue ultimately operated by threadLocalMap.set () method
  • GET () : Get value corresponding TreadLocal
  • the Remove () : delete the key-value from the ThreadLocalMap
//ThreadLocal的get()方法
public T get() {
	//得到当前线程
	Thread var1 = Thread.currentThread();
	//得到当前线程的ThreadLocalMap对象
	ThreadLocal.ThreadLocalMap var2 = this.getMap(var1);
	//如果ThreadLocal没有被显式set()过,则调用initialValue()先初始化
	if (var2 != null) {
		ThreadLocal.ThreadLocalMap.Entry var3 = var2.getEntry(this);
		if (var3 != null) {
			Object var4 = var3.value;
			return var4;
		}
	}
	//调用initialValue()先初始化
	return this.setInitialValue();
}

//ThreadLocal的set()方法
public void set(T var1) {
	Thread var2 = Thread.currentThread();
	ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
	if (var3 != null) {
		//说明不是第一次set,直接覆盖,this表示ThreadLocal
		var3.set(this, var1);
	} else {
		//说明是第一次set,新建map
		this.createMap(var2, var1);
	}
}

//ThreadLocal的remove()方法
public void remove() {
	ThreadLocal.ThreadLocalMap var1 = this.getMap(Thread.currentThread());
	if (var1 != null) {
		//删除this对应的threadLocal的value
		var1.remove(this);
	}
}

books 4.ThreadLocal Precautions

Uses the concept of threadLocal incorrectly can cause memory leaks, talking about this issue need to look at memory leaks and weak references:

  • Memory leak: the object is no longer used but did not release the memory space
  • Objects four kinds of references:

    1. Strong reference: ubiquitous means code, similar to the Object obj = new Object () of such a reference, as long as there are still strong references, garbage collector never recovered off the referenced object;

    2. Soft Quote: description of some non-essential but also with objects, for soft references associated with the object, before memory overflow exception will happen to these objects will be included in the scope of recovery in a second recovery, if the second once recovery is not enough memory, the memory will throw an overflow exception;

    3. Weak References: Description nonessential objects, also cited weaker than the strength of softer, is associated with a weak reference to an object can only survive until the next garbage collection occurs, the next garbage collection regardless of whether sufficient memory will only be a weak recovery off reference objects associated;

    4. imaginary reference: not affect the survival time of the object can not be acquired by a phantom reference object instance;

Speaking of the operation is essentially ThreadLocal above ThreadLocalMap, we look into the ThreadLocalMap Source:

key is weak references type

That's key for each Entry is weak reference types, value is the type of strong references, conduct business under normal circumstances worker executed, may be out of the normal GC, including key and value; but if the thread has not terminated such as permanent threads in the pool has not ended, then the key corresponding to the value it has not been recovered, because the key is weak references have been GC, but because it is a strong reference value has not been GC, leading to OOM. GCROOT simple chain of references follows: Thread -> TreadLocalMap -> Entry (null, value) -> value, i.e. there has been a strong value between the reference relationship and Thread .

JDK designers have taken into account this problem, in the ThreadLocal set () / remove () methods, if the Entry of the key is null, the value will be set to null value to help the GC. However, due to the worker thread has been, our business has been processed, it is generally not go to explicitly call these methods.

  • Correct posture at the end of the business process should remove explicit call () method, delete the corresponding key-value
  • Generic ThreadLocal the get () method must initialValue return value () method or set (T var1) corresponding method

The final step is to combine the specific business scenarios, weigh the use of locks, or the use of ThreadLocal or otherwise, not to use ThreadLocal and use ThreadLocal.


books Extended reading: the Java thread pool Comments

Published 202 original articles · won praise 571 · Views 1.47 million +

Guess you like

Origin blog.csdn.net/fanrenxiang/article/details/104261683