ThreadLocal is that simple

foreword

What I want to study today is ThreadLocal, which I came into contact with when I was learning JavaWeb basics a year ago. At that time, the first blog post ThreadLocal found on baidu. In the comments, many developers thought that the blogger misunderstood and gave Many relevant links to correct (but the original blogger may not be on the blog, and has not been modified). I also went to study it, but unfortunately there was no habit that was recorded at that time. Until now, I only remember some furs that I learned at that time.

Therefore, it is very important to make some technical records~ At the same time, ThreadLocal is also a very common interview question, and it is also a necessary knowledge point for Java developers~

Of course, if I have made a mistake, please forgive me, and welcome to leave a message under the comments to correct me~

1. What is ThreadLocal

Disclaimer: This article uses JDK 1.8

First, let's take a look at the JDK documentation:


/**
 * 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).
 * 
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.
 */  	
	

Combined with my summary, it can be understood as follows: ThreadLocal provides thread local variables, and each thread can operate on this local variable through set()sum get(), but it will not conflict with the local variables of other threads, realizing the data isolation of threads ~ .

In short: the variable filled in ThreadLocal belongs to the current thread, and the variable is isolated from other threads.

2. Why learn ThreadLocal?

It can be concluded from the above: ThreadLocal allows us to have the variables of the current thread, so what is the use of this function? ? ?

2.1 Manage Connection

**The most typical one is to manage the connection of the database: **When I was learning JDBC, I wrote a simple database connection pool for the convenience of operation. The reason for needing a database connection pool is also very simple. Frequently creating and closing Connections is a very expensive task. resource operation, so you need to create a database connection pool~

So, how to manage the connection of the database connection pool? ? We leave it to ThreadLocal to manage. Why leave it to manage it? ? ThreadLocal can realize that the operation of the current thread uses the same Connection, which ensures the transaction!

The code written at the time:


public class DBUtil {
    //数据库连接池
    private static BasicDataSource source;

    //为不同的线程管理连接
    private static ThreadLocal<Connection> local;


    static {
        try {
            //加载配置文件
            Properties properties = new Properties();

            //获取读取流
            InputStream stream = DBUtil.class.getClassLoader().getResourceAsStream("连接池/config.properties");

            //从配置文件中读取数据
            properties.load(stream);

            //关闭流
            stream.close();

            //初始化连接池
            source = new BasicDataSource();

            //设置驱动
            source.setDriverClassName(properties.getProperty("driver"));

            //设置url
            source.setUrl(properties.getProperty("url"));

            //设置用户名
            source.setUsername(properties.getProperty("user"));

            //设置密码
            source.setPassword(properties.getProperty("pwd"));

            //设置初始连接数量
            source.setInitialSize(Integer.parseInt(properties.getProperty("initsize")));

            //设置最大的连接数量
            source.setMaxActive(Integer.parseInt(properties.getProperty("maxactive")));

            //设置最长的等待时间
            source.setMaxWait(Integer.parseInt(properties.getProperty("maxwait")));

            //设置最小空闲数
            source.setMinIdle(Integer.parseInt(properties.getProperty("minidle")));

            //初始化线程本地
            local = new ThreadLocal<>();


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {

        if(local.get()!=null){
            return local.get();
        }else{
        
          //获取Connection对象
          Connection connection = source.getConnection();

          //把Connection放进ThreadLocal里面
          local.set(connection);

          //返回Connection对象
          return connection;
       }
    }

    //关闭数据库连接
    public static void closeConnection() {
        //从线程中拿到Connection对象
        Connection connection = local.get();

        try {
            if (connection != null) {
                //恢复连接为自动提交
                connection.setAutoCommit(true);

                //这里不是真的把连接关了,只是将该连接归还给连接池
                connection.close();

                //既然连接已经归还给连接池了,ThreadLocal保存的Connction对象也已经没用了
                local.remove();

            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


}

Similarly, Hibernate uses the same method for Connection management (using ThreadLocal, of course, Hibernate's implementation is more powerful)~

2.2 Avoid some parameter passing

To avoid the passing of some parameters, you can refer to Cookie and Session:

  • Whenever I visit a page, the browser will help us find the corresponding cookie from the hard disk and send it.
  • The browser is very smart. It will not send cookies from other websites, but only take cookies from the current website.

The browser is equivalent to our ThreadLocal, it only sends the cookies that exist in our current browser (the local variables of ThreadLocal), and different browsers isolate cookies (Chrome, Opera, IE cookies are isolated [in Chrome] Logged in, you have to log in again in IE]), the same: ThreadLocal variables between threads are also isolated....

Does the above avoid the passing of parameters? ? Actually avoided. Cookies are not passed by us manually, and do not need to be written <input name= cookie/>to pass parameters...

The same is true in programming: in daily life, when we go to do business, we may use ID cards and various documents in many places. It is very troublesome for us to take out every time.


    // 咨询时要用身份证,学生证,房产证等等....
    public void consult(IdCard idCard,StudentCard studentCard,HourseCard hourseCard){

    }

    // 办理时还要用身份证,学生证,房产证等等....
    public void manage(IdCard idCard,StudentCard studentCard,HourseCard hourseCard) {

    }

    //......

And if ThreadLocal is used, ThreadLocal is equivalent to an organization, and the ThreadLocal organization records that you have so many documents. When you need it, you don't have to pay for it yourself, you can just ask the agency to get it.

When consulting, I told the agency: Come, give him my ID card, real estate certificate, and student ID card. At the time of processing, I told the agency: Come, give him my ID card, real estate certificate, and student ID card. ...


    // 咨询时要用身份证,学生证,房产证等等....
    public void consult(){

        threadLocal.get();
    }

    // 办理时还要用身份证,学生证,房产证等等....
    public void takePlane() {
        threadLocal.get();
    }

Wouldn't it be more convenient than paying for it yourself.

Of course, ThreadLocal may have other better functions. If you know, you can leave a message in the comments~~~

Third, the principle of ThreadLocal implementation

If you want to better understand ThreadLocal, you have to look at how it is implemented~~~

Disclaimer: This article uses JDK 1.8

First, let's take a look at the set() method of ThreadLocal, because we generally use the new object, so we set the object inside.


    public void set(T value) {

		// 得到当前线程对象
        Thread t = Thread.currentThread();
		
		// 这里获取ThreadLocalMap
        ThreadLocalMap map = getMap(t);

		// 如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

There is a ThreadLocalMap on it, let's see what is this?


static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
		//....很长
}

From the above we can find that ThreadLocalMap is an inner class of ThreadLocal. Use the Entry class for storage

Our values ​​are stored in this Map, and the key is the current ThreadLocal object !

If the Map does not exist, initialize one:


    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

If the Map exists, get it from Thread !


    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

Thread maintains the ThreadLocalMap variable


    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null

As can be seen from the above, ThreadLocalMap is written using inner classes in ThreadLocal, but the reference to the object is in Thread !

So we can conclude: Thread maintains a Map such as ThreadLocalMap for each thread, and the key of ThreadLocalMap is the ThreadLocal object itself, and the value is the object to be stored

With the above foundation, it is not difficult to understand the get() method at all:


    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();
    }

3.1 ThreadLocal principle summary

  1. Each Thread maintains a reference to a ThreadLocalMap
  2. ThreadLocalMap is the inner class of ThreadLocal, which uses Entry for storage
  3. When calling the set() method of ThreadLocal, it actually sets the value to ThreadLocalMap, the key is the ThreadLocal object, and the value is the passed in object
  4. When calling the get() method of ThreadLocal, it actually gets the value from ThreadLocalMap, and the key is the ThreadLocal object
  5. ThreadLocal itself does not store the value , it is only used as a key for the thread to get the value from the ThreadLocalMap .

Because of this principle, ThreadLocal can achieve "data isolation" and obtain the local variable value of the current thread, without being affected by other threads~

Fourth, avoid memory leaks

Let's take a look at the object relationship reference diagram of ThreadLocal:

 

 

The root cause of ThreadLocal memory leaks is: since the life cycle of ThreadLocalMap is as long as Thread, if the corresponding key is not manually deleted, it will cause memory leaks, not because of weak references .

If you want to avoid memory leaks, you must manually remove() it !

V. Summary

There are really countless blog posts in this area of ​​ThreadLocal, and there are many, many just a search ~ Standing on the shoulders of predecessors and summarizing this blog post~

The last thing to remember is: ThreadLocal is designed to have its own variables in the current thread, not to solve the problem of concurrency or shared variables

If you don't have enough fun, and you don't think you're in-depth enough, you can refer to the link below. Many bloggers have also developed some extended knowledge, so I won't expand them one by one~

Reference blog post:

 

 

If there are any mistakes in the article, please correct me, and we can communicate with each other. Students who are accustomed to reading technical articles on WeChat and want to get more Java resources can follow WeChat public account: Java3y


 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325437244&siteId=291194637