概念:
ThreadLocal并不是一个Thread,而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路
原理:
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
源码分析:
- Implements a thread-local storage, that is, a variable for which each thread
- has its own value. All threads share the same {@code ThreadLocal} object,
- but each sees a different value when accessing it, and changes made by one
- thread do not affect the other threads. The implementation supports
*实现一个线程本地存储,每个线程的一个变量
*有它自己的价值。所有线程共享相同的ThreadLocal对象,
*但每看到一个不同的价值当访问它,和更改
*线程不会影响其他线程
public class ThreadLocal<T> {
可以看出threadlocal是一个范型类,这标志着threadlocal可以存储所有数据
set()方法:
public void set(T value) {
Thread currentThread = Thread.currentThread();//获取当前运行的线程
Values values = values(currentThread);//返回一个存储类
if (values == null) {
values = initializeValues(currentThread);//初始化return new Values()
}
values.put(this, value);//插入
}
首先会获取当前线程,根据当前线程获取Values存储类,再调用values存储类中的put方法,将内容存储到Values内部类的table数组的下标key.reference中。
values.put():
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
把values的值传入到一个table数组的key.reference的下一个下标中
table就以key,value的形式存储了线程的本地变量,偶数位放key,基数位放value。
两个地方不太清楚
1.key.reference 是什么值
2.for (int index = key.hash & mask;; index = next(index)) 条件是什么?
get():
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
方法返回一个当前线程的当前value值,如果这个值没有初始化,那么会通过initialValue();返回一个null。
remove():
void remove(ThreadLocal<?> key) {
cleanUp();
for (int index = key.hash & mask;; index = next(index)) {
Object reference = table[index];
if (reference == key.reference) {
// Success!
table[index] = TOMBSTONE;
table[index + 1] = null;
tombstones++;
size--;
return;
}
if (reference == null) {
// No entry found.
return;
}
}
}
内存释放,手动释放当前线程的存储的值。
ThreadLocal和线程同步机制相比有什么优势呢?
对于同步机制中来说,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
对于ThreadLocal来说,它为每一个线程提供一个独立的变量副本
,从而隔离了多个线 程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编 写多线程代码时,可以把不安全的变量封装进ThreadLocal,从另一个角度来解决多线程的并发访问。