为了可以解决并发问题,可以考虑将并发问题的源头共享变量变得不共享

没有共享,就没有伤害

在多线程中将共享变量变得不共享,可以考虑针对每个线程,都让其拥有自己的变量,彼此不共享,就没有并发问题了

在Java中,提供的符合上面的思想的实现为 线程本地存储 ThreadLoacl

ThreadLoacl的简单使用可以看下面的示例代码

static class ThreadId {

static final AtomicLong nextId = new AtomicLong(0);

//定义ThreadLocal变量

static final ThreadLocal<Long> tl = ThreadLocal.withInitial(() -> nextId.getAndIncrement());

//此方法会为每个线程分配一个唯一的Id

static long get() {

return tl.get();

}

}

如果是同一个线程调用这个类,会获得相同的Id,不同的线程获得不同的Id

那么对于其的工作原理:

ThreadLoacl的目标是让不同的线程有不同的变量V,那么可以设置一个Map,其的key是线程,Value是每个线程对应的变量

图片

class MyThreadLocal<T> {

Map<Thread, T> locals = new ConcurrentHashMap<>();

//获取线程变量

T get() {

return locals.get(

Thread.currentThread());

}

//设置线程变量

void set(T t) {

locals.put(

Thread.currentThread(), t);

}

}

在我们的猜想中,ThreadLocal的实现可以基本如上

但是实际上,对于ThreadLocal的实现就没有这么简单了

其实现为,在Thread这个类中有一个私有属性 threadLocals,其类型就是ThreadLocalMap,其key为ThreadLocal

class Thread {

//内部持有ThreadLocalMap

ThreadLocal.ThreadLocalMap

threadLocals;

}

class ThreadLocal<T>{

public T get() {

//首先获取线程持有的

//ThreadLocalMap

ThreadLocalMap map = Thread.currentThread().threadLocals;

//在ThreadLocalMap中

//查找变量

Entry e = map.getEntry(this);

return e.value;

}

static class ThreadLocalMap{

//内部是数组而不是Map

Entry[] table;

//根据ThreadLocal查找Entry

Entry getEntry(ThreadLocal key){

//省略查找逻辑

}

//Entry定义

static class Entry extends

WeakReference<ThreadLocal>{

Object value;

}

}

}

ThreadLocal只是作为一个工具类,内部不持有和线程相关的数据,因为是一个静态类型的数据,将所有线程相关的线程存储的Thread里面,

而且不容易产生内存泄漏,将ThreadLocalMap交给Thread持有,而且引用还是弱引用,只要Thread对象可以被回收,那么ThreadLocalMap就能被回收,防止内存泄漏,但是还是可能出现内存泄漏的问题

如果选择实现一个额外的类去维护,那么即使Vavle的生命周期结束了,由于在整个Map的Entry中是被强引用,也无法被回收掉

而且其有一个实现子类 InheritableThreadLocal来支持从子线程继承父线程的线程变量,但是不建议使用,因为可能导致业务逻辑混乱

总而言之,线程本地存储模式是一个避免共享变量的方案,没有共享,就没有并发问题,这是一个解决并发问题的常用思路

发表评论

邮箱地址不会被公开。 必填项已用*标注