Thread&ThreadLocal

Thread类

有许多重载的构造器

new Thread(……)

调用构造器,在构造器中会调用init方法

  在init方法中,调用currentThread()方法获得当前正在创建新线程的线程,并将新线程的daemon、priority、contextClassLoader、target、stackSize等设置为和创建新线程的线程一致。this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);(子线程继承父线程的相关设置)

  在init方法中,并不会初始化Thread类的ThreadLocal.ThreadLocalMap类型的成员变量,而是在当前线程中,调用了ThreadLocal的get或者set方法时,才会初始化该属性。

 

 

在当前线程中,可以创建一个或者多个ThreadLocal类型的变量

public class TestThreadLocal2 {
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
    private static ThreadLocal<String> threadLocal2 = new ThreadLocal<String>();
    @Test
    public void test(){
        for(int i=0;i<5;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    threadLocal.set((int) (Math.random()*100));
                    threadLocal2.set("------");
                    System.out.println(threadLocal.get()+threadLocal2.get()+"  "+Thread.currentThread().getName());;                
                }
            }).start();
        }
        threadLocal.set((int) (Math.random()*100));
        threadLocal2.set("======");
        System.out.println(threadLocal.get()+threadLocal2.get()+"  "+Thread.currentThread().getName());;

    }
}

 ThreadLocal

线程本地存储,每个线程都拥有自己的线程本地存储,线程私有,线程隔离

This class provides thread-local variables

每个线程都拥有独立的共享变量的副本,副本只对当前线程可见,当前线程对副本的修改不会影响其他线程

ThreadLocal实例通常定义为private static

只要线程还是活的并且ThreadLocal实例是可访问的,每个线程都保持对线程本地变量的副本的隐式引用

线程死亡之后,垃圾回收回回收线程的本地变量的所有副本

Thread类的threadLocals属性在调用ThreadLocal的get或者set方法时初始化

ThreadLocal是如何为每个线程创建共享变量的副本的?

  Thread类有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,threadLocals就用来存储每个线程的变量副本

  创建线程、线程初始化之后,threadLocals仍为null,在调用ThreadLocal对象的get或者set方法时,才会初始化threadLocals

  变量的副本实际存储在Thread类的threadLocals成员变量中

  threadLocals是ThreadLocal.ThreadLocalMap类型,ThreadLocalMap是ThreadLocal的静态内部类,成员内部类Entry

    存储当前线程的变量副本:private void set(ThreadLocal key, Object value)

    键:this,当前ThreadLocal类的实例,因为一个线程可能有多个ThreadLocal对象,所以使用ThreadLocal类的实例作为ThreadLocalMap的键

    值:value,要保存的变量的副本

    在当前线程中使用变量副本:private Entry getEntry(ThreadLocal key)       entry.value

  

 

1.get(调用ThreadLocal对象的get方法,获取当前线程的threadlocals成员变量中保存的键为this的value,该value值即为线程的变量副本)

(1)获取当前线程t   Thread.currentThread()

(2)调用getmap(t),获取与当前线程绑定的ThreadLocalMap对象map

  getMap方法:直接返回当前线程的成员变量threadLocals(ThreadLocal.ThreadLocalMap类型)

(3)如果map不为空,从map获取ThreadLocal在当前线程中保存的变量副本

  map.getEntry(this)     this是ThreadLocal的对象

    如果从map中找到了,直接返回

(4)如果map为空,或者从map中找不到键this对应的Entry

  调用setInitialValue()方法

  在setInitialValue()方法中

      首先,返回初始化值value,调用initialValue()(protected方法,返回null,因此,如果在set之前get,会空指针异常;一定要在get之前set,就应该在创建private static的ThreadLocal对象时,重写initialValue方法,返回一个初始化值)

      然后,如果map是空,调用createMap(t,value)初始化当前线程的threadlocals成员变量

         如果map不为空(当前map中,没有this对应的Entry),map.set(this,value)

 

2.set(value)(调用ThreadLocal对象的set方法,在当前线程的threadLocals成员变量中保存一个键为this,值为value的共享变量副本)

(1)获得当前线程t 

  Thread.currentThread();

(2)获得当前线程的ThreadLocalMap成员变量

  ThreadLocalMap map = getMap(t);(getMap方法直接返回t.threadLocals)

(3)map不为空

  map.set(this, value);

(4)map为空

  createMap(t, value);(在createMap方法中,初始化Thread类的threadLocals成员变量t.threadLocals = new ThreadLocalMap(this, firstValue);)

 

3.remove(调用ThreadLocal对象的remove方法,移除当前线程的threadlocals对象中键为this的entry)

(1)获得当前线程的ThreadLocalMap对象

  ThreadLocalMap m = getMap(Thread.currentThread());

(2)m.remove(this);

 

 ThreadLocalMap

ThreadLocal的静态内部类

Thread类的ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,该成员变量实际就是存储了当前线程的变量副本(map)

 Entry继承WeakReference

  弱引用问题

    ThreadLocal已经被回收了,ThreadLocalMap中可能存在键为null的键值对

  解决:将ThreadLocal定义为private static

     调用remove清除

原文地址:https://www.cnblogs.com/duanjiapingjy/p/9444483.html