关于多线程并发安全控制,我们第一反应都是用锁,但是无论是哪种锁,多多少少都对性能都有一定得影响。那么有什么方法既能保证共享资源的线程安全,又能避免锁呢?答案就是ThreadLocal。ThreadLocal可以解释成线程的局部变量,即每个线程都有变量的副本,一个线程对变量的访问都是对线程自己变量的访问,对其他线程并无影响,也就保证了对于共享资源的线程安全,又避免用锁。
ThreadLocal的基本使用
创建一个ThreadLocal对象:
public class ThreadLocalTest {
private ThreadLocal<Integer> localInt = new ThreadLocal<>();
public void testLocal(){
for (int i = 0; i < 10; i++) {
final int value = i * 2;
new Thread(new Runnable() {
@Override
public void run() {
localInt.set(value);
System.out.println(Thread.currentThread().getName() + ":" +localInt.get());
localInt.remove();
}
}).start();
}
}
public static void main(String[] args) {
ThreadLocalTest localTest = new ThreadLocalTest();
localTest.testLocal();
}
}
ThreadLocal的构造方法无参数,由于ThreadLocal是一个泛型类,这里指定了localInt的类型为整数。ThreadLocal常用的方法有:
- set(obj):向当前线程中存储数据
- get():获取当前线程中的数据
- remove():删除当前线程中的数据
由于ThreadLocal里设置的值,只有当前线程自己看得见,这意味着你不可能通过其他线程为它初始化值。为了弥补这一点,ThreadLocal提供了一个withInitial()方法统一初始化所有线程的ThreadLocal的值:
private ThreadLocal<Integer> localInt = ThreadLocal.withInitial(() -> 6);
ThreadLocal的实现原理
ThreadLocal可以做到变量的线程隔离(线程只可见自己的变量),那么它是怎么做到的呢?我们先从ThreadLocal的set方法开始入手:
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); //就是map的设值
else
createMap(t, value); //map为空先创建再设值
}
从上面的代码我们可以知道ThreadLocalMap是实现的关键。看一下getMap(t) 的实现:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
很简单,就是从当前线程中获取ThreadLocalMap。到这里我们可以大概猜测到,所谓的ThreadLocal变量就是保存在每个线程的map中的。这个map就是Thread对象中的threadLocals字段:
public class Thread implements Runnable {
...
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
关于ThreadLocalMap
未完待续…