1. Java基础(1)——ThreadLocal
1.1. ThreadLocal
ThreadLocal是一个泛型类,当我们在一个类中声明一个字段: private ThreadLocal<Foo> threadLocalFoo = new ThreadLocal<>();
时,这时候,即使不同的线程持有了该类的同一个实例,那么它们在访问该实例的 threadLocalFoo
的时候访问的是不同的Foo对象,这些Foo对象和这些线程是一一对应的关系,并被这些线程所私有,因此每个线程不需要对自己从 threadLocalFoo
获得的Foo实例进行加锁(加锁也没用啊),这种无锁化的设计提高了并行能力,但注意ThreadLocal并不是万能的,有些场景可以使用ThreadLocal(比如Spring中的事务),但有些场景它的语义就是必须对同一个对象实例进行加锁后独占地访问,比如单例模式,这种ThreadLocal就起不了作用了。
当然ThreadLocal还提供了 initialValue
这个protected方法,用来创建声明的泛型类型对象,因此我们还可以以下面这种方式来声明一个thread local:
ThreadLocal<Foo> threadLocal = new ThreadLocal<Foo>(){ @Override protected Foo initialValue() { return new Foo(); } };
同时ThreadLocal还提供了一个 withInitial
静态方法,该方法接收一个相同泛型类型的Supplier,返回ThreadLocal。
Java的每个Thread实例中,都有一个ThreadLocalMap类型的实例字段,它存放了该线程所用到过的所有 ThreadLocal
式样的实例对象,比如,有个类中声明了这个字段 private ThreadLocal<Foo> threadLocalFoo = new ThreadLocal<>();
,虽然它的一个实例被多个线程持有,但这些线程不一定都访问过这个实例的 threadLocalFoo
字段,只有访问过这个字段的Thread,它的thread local map中才会存Foo对象(以Entry的方式存,key为该ThreadLocal实例(共享),value为每个线程自己持有的Foo对象(私有))。
注意,我们使用ThreadLocal的是因为有些对象每个线程都可以持有一份,然后我们才使用ThreadLocal来避免同一个对象的实例方法的并发操作,但这样的话我们要谨防ThreadLocal的退化:如果使用它的时候,用之前都是set,之后就remove,那么相当于每访问一次ThreadLocal都要创建出一个新的对象出来,这样发挥不出ThreadLocal节省对象数量的作用。ThreadLocal一般被声明为static字段。
1.1.1. get方法
如果当前的Thread中的thread local map字段不空,并且其中存的有对应的对象,那么返回。
如果thread local map字段不空,但是没有存对应的对象,那么使用initialValue创建对象,然后将它和该ThreadLocal实例,打包成Entry放入当前的thread local map中,返回创建的对象。
如果thread local map字段为空,那么首先创建对象,然后创建该线程的thread local map,然后再存Entry,再返回创建的对象。
总而言之呢,get方法就是说返回的对象都必须从当前线程的thread local map中取,thread local map没创建,就创建thread local map,创建了但里面没有需要的对象,那么就创建对象并将其塞进去,反正必须从thread local map中拿就对了。