There is still a difference between ThreadLocal and thread member variables. The ThreadLocal class provides thread local variables. This local variable is different from general member variables. When the ThreadLocal variable is used by multiple threads, each thread can only get one copy of the variable. This is a description in the Java API. By reading the API source code, I found that it is not a copy. What is the concept of a copy? Clones? Or something else, too vague.
To be precise, the registry (Map<Thread,T>) inside the variable of type ThreadLocal has changed, but the variable of type ThreadLocal itself is indeed one, and this is the essence!
Here is an example:
1. Standard examples
The MyThreadLocal class is defined, and its object tlt is created, and it is used by four threads. As a result, the tlt variables of the four threads do not share. The second is to use their own, which shows that the four threads use a copy of tlt (clone).
/** * Use ThreadLocal class*/ public class MyThreadLocal { //Define a ThreadLocal variable to save int or Integer data private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 0; } }; public Integer getNextNum() { //Get the value of tl and add 1, and update the value of t1 tl.set(tl.get() + 1); return tl.get(); } } /** * Test thread*/ public class TestThread extends Thread { private MyThreadLocal tlt = new MyThreadLocal(); public TestThread(MyThreadLocal tlt) { this.tlt = tlt; } @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "/t" + tlt.getNextNum()); } } } /** * ThreadLocal test*/ public class Test { public static void main(String[] args) { MyThreadLocal tlt = new MyThreadLocal(); Thread t1 = new TestThread(tlt); Thread t2 = new TestThread(tlt); Thread t3 = new TestThread(tlt); Thread t4 = new TestThread(tlt); t1.start(); t2.start(); t3.start(); t4.start(); } }
It can be seen that the three threads are numbered independently and do not affect each other:
Thread-0 1 Thread-1 1 Thread-0 2 Thread-1 2 Thread-0 3 Thread-1 3 Thread-2 1 Thread-3 1 Thread-2 2 Thread-3 2 Thread-2 3 Thread-3 3 Process finished with exit code 0
The tlt object is one, and the nonsense tl object is also one, because the combination relationship is one-to-one. However, as the number of threads increases, many Integer objects will be created. It's just that Integer and int are already common. So I can't feel the object properties of Integer.
2. Don't use ThreadLocal
If you don't use ThreadLocal, you just need to redefine the MyThreadLocal class as:
/** * Use ThreadLocal class*/ public class MyThreadLocal { private Integer t1 = 0; public Integer getNextNum(){ return t1=t1+1; } // Define a ThreadLocal variable to save int or Integer data// private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() { // @Override // protected Integer initialValue() { // return 0; // } // }; // // public Integer getNextNum() { // //Get the value of tl and add 1, and update the value of t1// tl.set(tl.get() + 1); // return tl.get(); // } }
Then run the test:
Thread-2 1 Thread-2 2 Thread-1 4 Thread-1 6 Thread-3 3 Thread-3 9 Thread-3 10 Thread-1 8 Thread-0 7 Thread-0 11 Thread-0 12 Thread-2 5 Process finished with exit code 0
From here, we can see that the four threads share the tlt variable, and each thread directly modifies the tlt's properties.
3. Realize ThreadLocal by yourself
package com.lavasoft.test2; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Use ThreadLocal class*/ public class MyThreadLocal { //Define a ThreadLocal variable to save int or Integer data private com.lavasoft.test2.ThreadLocal<Integer> tl = new com.lavasoft.test2.ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 0; } }; public Integer getNextNum() { //Get the value of tl and add 1, and update the value of t1 tl.set(tl.get() + 1); return tl.get(); } } class ThreadLocal<T> { private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>()); public ThreadLocal() { } protected T initialValue() { return null; } public T get() { Thread t = Thread.currentThread(); T obj = map.get(t); if (obj == null && !map.containsKey(t)) { obj = initialValue(); map.put(t, obj); } return obj; } public void set(T value) { map.put(Thread.currentThread(), value); } public void remove() { map.remove(Thread.currentThread()); } }
Run the test:
Thread-0 1 Thread-0 2 Thread-0 3 Thread-2 1 Thread-2 2 Thread-3 1 Thread-2 3 Thread-3 2 Thread-1 1 Thread-3 3 Thread-1 2 Thread-1 3 Process finished with exit code 0
Surprisingly, this copycat version of ThreadLocal also works well, implementing the function of ThreadLocal in Java API.
4. See the essence through phenomena
In fact, from a program perspective, the tlt variable is indeed one, without a doubt. But why don’t the printed numbers affect each other?
Is it because of using Integer? -----no.
The reason is: protected T initialValue() and get(), because when each thread calls get(), it will create it if it does not exist in the map. When it is called, a new variable is created with type T. Every time they are newly created, of course, each one uses them without any effect on each other.
In order to see the essence clearly, replace Integer and rewrite some classes:
package com.lavasoft.test2; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Use ThreadLocal class*/ public class MyThreadLocal { //Define a ThreadLocal variable to save int or Integer data// private ThreadLocal<Bean> tl = new ThreadLocal<Bean>() { private com.lavasoft.test2.ThreadLocal<Bean> tl = new com.lavasoft.test2.ThreadLocal<Bean>() { @Override protected Bean initialValue() { return new Bean(); } }; @Override public String toString() { return "MyThreadLocal{" + "tl=" + tl + '}'; } public Bean getBean() { return tl.get(); } } class ThreadLocal<T> { private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>()); public ThreadLocal() { } protected T initialValue() { return null; } public T get() { Thread t = Thread.currentThread(); T obj = map.get(t); if (obj == null && !map.containsKey(t)) { obj = initialValue(); map.put(t, obj); } return obj; } public void set(T value) { map.put(Thread.currentThread(), value); } public void remove() { map.remove(Thread.currentThread()); } } package com.lavasoft.test2; /** * Test Bean */ public class Bean { private String id = "0"; private String name = "none"; public Bean() { } public Bean(String id, String name) { this.id = id; this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String showinfo() { return "Bean{" + "id='" + id + '/'' + ", name='" + name + '/'' + '}'; } } package com.lavasoft.test2; /** * Test thread*/ public class TestThread extends Thread { private MyThreadLocal tlt = new MyThreadLocal(); public TestThread(MyThreadLocal tlt) { this.tlt = tlt; } @Override public void run() { System.out.println(">>>>:" + tlt); for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "/t" +tlt.getBean()+"/t"+tlt.getBean().showinfo()); } } }
Then run the test:
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d} >>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d} >>>>>:MyThreadLocal{tl=com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'} Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id='0', name='none'} Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id='0', name='none'} Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id='0', name='none'} Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id='0', name='none'} Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id='0', name='none'} Thread-0 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'} Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id='0', name='none'} Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id='0', name='none'} Thread-1 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'} Thread-0 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'} Thread-0 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'} Thread-1 com.lavasoft.test2.Bean@291aff Bean{id='0', name='none'} Process finished with exit code 0
It is clear from the printing results that the tlt object of MyThreadLocal is indeed one, and the tl object of ThreadLocal in the tlt object is also one. However, when t1t is used for each thread, the thread will recreate the Bean object and add it to the ThreadLocal Map for use.
Several misunderstandings about ThreadLocal:
1. ThreadLocal is an implementation of java threads
ThreadLocal is indeed related to java threads, but it is not an implementation of java threads, it is just used to maintain local variables. For each thread, it provides its own variable version, mainly to avoid thread conflicts, and each thread maintains its own version. Be independent of each other, and modification will not affect each other.
2. ThreadLocal is relative to each session
ThreadLocal, as the name implies, is aimed at threads. In Java web programming, each user has its own session identifier from the beginning to the end of the session. But ThreadLocal is not on the session layer. In fact, Threadlocal is independent of the user session. It is a server-side behavior. Whenever a server generates a new thread, it maintains its own ThreadLocal.
Regarding this misunderstanding, I personally believe that it should be the result of the developer's local test based on some application servers. As we all know, general application servers maintain a set of thread pools, that is, for each access, a new thread does not necessarily generate. Instead, I have a thread cache pool. For access, first find the existing threads from the cache pool. If they have used up all, then a new thread will be generated.
Therefore, since the developer is usually the only one who is testing himself, the server burden is very small, which leads to the sharing of the same thread every time the access is, resulting in the misunderstanding: each session has a ThreadLocal
3. ThreadLocal is relative to each thread. Each time the user accesses, there will be a new ThreadLocal.
Theoretically, ThreadLocal is indeed relative to each thread, each thread will have its own ThreadLocal. But as mentioned above, general application servers maintain a set of thread pools. Therefore, different users may receive the same thread. Therefore, when doing the HeadLocal-based, you need to be careful to avoid the cache of ThreadLocal variables, causing other threads to access the thread variables
4. For each user access, ThreadLocal can be used multiple times.
It can be said that ThreadLocal is a double-edged sword, and can have very good results if used. However, if ThreadLocal is not used well, it will be the same as global variables. The code cannot be reused and cannot be tested independently. Because some classes that could have been reused now rely on the ThreadLocal variable. If there is no ThreadLocal, these classes become unavailable. I personally think that ThreadLocal is used well and is worth referring to
1. Store the current session user: quake want jert
2. Store some context variables, such as webwork's ActionContext
3. Store sessions, such as Spring hibernate orm sessions