概念
ThreadLocal称之为线程本地变量。在多线程的情况下每一个线程保存一份共享变量的副本,那么访问的时候只会访问自己保存的那个副本,而不会影响其他线程的操作。
使用实例
下面的代码创建5个线程,然后给成员变量每次加5.
public class ThreadLocalMini {
private static Integer num=0;
public static void main(String[] args) {
Thread[] threads=new Thread[5];
for (int i=0;i<threads.length;i++){
threads[i]=new Thread(()->{
num+=5;
System.out.println(Thread.currentThread().getName()+":"+num);
},"Thread-"+i);
}
for (int i=0;i<threads.length;i++){
threads[i].start();
}
}
}
打印结果:
Thread-0:5
Thread-1:10
Thread-2:15
Thread-3:20
Thread-4:25
但是我希望每一个线程打印的都是5,不要在原来的基础上加5.可以使用ThreadLocal去改造。
public class ThreadLocalMini {
private static ThreadLocal<Integer> num2=new ThreadLocal(){
/**初始化*/
@Override
protected Object initialValue() {
return 0;
}
};
public static void main(String[] args) {
Thread[] threads=new Thread[5];
for (int i=0;i<threads.length;i++){
threads[i]=new Thread(()->{
int res = num2.get().intValue();
res+=5;
num2.set(res);
System.out.println(Thread.currentThread().getName()+":"+num2.get());
},"Thread-"+i);
}
for (int i=0;i<threads.length;i++){
threads[i].start();
}
}
}
打印结果如下:
Thread-0:5
Thread-3:5
Thread-4:5
Thread-2:5
Thread-1:5
设计原理
从概念以及使用原理上来看,我们必须保证每个线程之间互相不影响。即线程隔离,其次每个线程还要对应保存数据。那么我们可以使用ConcurrentHashMap来做一个简单的实现。
/**
* @Author: gosaint
* @Description:
* @Date Created in 19:31 2020/4/17
* @Modified By:
*
* 我们实现4个方法 put(Object obj),get(),remove(),init()方法
*/
public class SimpleThreadLocal {
/**
* map存储的key为当前线程,value为存储的变量
*/
private static ConcurrentHashMap<Thread,Object> map=new ConcurrentHashMap<>();
public void put(Object obj){
map.put(Thread.currentThread(),obj);
}
public Object get(){
Thread thread = Thread.currentThread();
Object o = map.get(thread);
if(o==null && !map.containsKey(thread)){
o=initialValue();
map.put(thread,o);
}
return o;
}
public void remove(){
map.remove(Thread.currentThread());
}
protected Object initialValue() {
return null;
}
}
set源码
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
这里和我设想方案有点差别。它使用了ThreadLocalMap来保存数据,我们可以看下ThreadLocalMap的内部结构。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
本质上还是一个key-value形式的Map数据结构。

在这里需要说明的是ThreadLocalMap也是类似于HashMap一样的数据结构。
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
上面的几个属性主要是确认key在hash表中的位置,ThreadLocalMap使用的是线性探测法解决hash冲突,每次在原来的基础上加0x61c88647这个数字去解决的。而且nextHashCode 这个变量也是原子操作。
本文详细介绍了ThreadLocal的概念及其在多线程环境中的作用,通过对比普通变量和ThreadLocal变量的使用,展示了如何避免线程间的数据冲突。同时,文章深入探讨了ThreadLocal的设计原理和内部实现机制,包括其如何利用ThreadLocalMap进行数据存储,以及解决hash冲突的方法。
850

被折叠的 条评论
为什么被折叠?



