1. 简单使用
package thread;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ThreadLocalTest {
//定义一个threadLocal共享亦是
private static final ThreadLocal<String> userIdThreadLocal = new ThreadLocal<String>();
public static void main(String[] args) {
String mainUserId = UUID.randomUUID().toString();
System.out.println("main thread userId set:" + mainUserId);
//主线程中设置userId
userIdThreadLocal.set(mainUserId);
Thread t1 = new Thread(()->{
String t1UserId = UUID.randomUUID().toString();
System.out.println("t1 thread userId set:" + mainUserId);
//t1线程中设置userId
userIdThreadLocal.set(t1UserId);
try {
TimeUnit.MICROSECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
//t1线程中打印userId
System.out.println("t1 thread userId get:" + userIdThreadLocal.get());
});
t1.start();
//主线程中打印userId
System.out.println("main thread userId get:" + userIdThreadLocal.get());
}
}
//main thread userId set:c7eee748-a2af-4686-9b26-ba930aa3c2fd
//main thread userId get:c7eee748-a2af-4686-9b26-ba930aa3c2fd
//t1 thread userId set:c7eee748-a2af-4686-9b26-ba930aa3c2fd
//t1 thread userId get:b43f281f-19b0-4b4f-92b4-3f10cba3612d
可以看到,同一个线程中设置完userId,在线程中的其他地方可以读取出来使用
2. 实现原理
我们先看一下ThreadLocal中,上面用到的两个方法:get(),set()
这里我们要先看一个ThreadLocal中的嵌套类ThreadLocalMap
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;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
}
说明:
- 我们可以看到ThreadLocalMap内部还嵌套了一个Entry的内部类,它是继承了弱引用,Entry本身只有一个value值
- ThreadLocalMap中,有一个Entry类型的数组,Entry[] table,这里保存着各ThreadLocal的key,以及对应的value值
下面再看一下这两个方法:
/**
* 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 {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread(); //获得当前线程
ThreadLocalMap map = getMap(t); //根据线程获得threadLocalMap变量
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //获得当前threadlocal对应的entry值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result; //返回entry中的value值
}
}
return setInitialValue();
}
/**
* 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 {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //返回当前线程中的threadlocalMap变量
if (map != null)
map.set(this, value); //根据当前threadlocal键,设置value值
else
createMap(t, value);
}
从上面可以看出,我们根据线程,就可以得到threadLocalMap变量,
我们看一下线程的源码可以看到如下两行定义:
image.png
也就是说,实际threadLocalMap值是存储在当前线程中的
3. 总结
- 与synchronized锁的机制不同,threadLocal主要是在每个线程中保存着一份entry值,包含着当前threadlocal和对应的value值
- 每次取值时,都可以从当前线程的threadLocalMap中,根据threadLocal取得对应的value值
网友评论