ThreadLocal

26 篇文章 0 订阅
25 篇文章 0 订阅

ThreadLocal引入

该类主要实现每一个线程都有自己共享变量。该类提供了线程局部变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本

ThreadLocal使用
        public static void main(String[] args) {
        ThreadLocal tl = new ThreadLocal<>();
        if(tl.get()==null) {
            System.out.println("是空值哦");
            tl.set("我的值");
        }
        System.out.println(tl.get());
        System.out.println(tl.get());
    }

常用的方法通过set、get方法去操作当前线程的变量。

ThreadLocal get第一次返回为null问题

重写initialValue。

static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {//这里就相当于构造一个匿名类继承了ThreadLocal,并且重写了initialValue方法。

        protected Integer initialValue() {
            return 999;
        };
    };
        public static void main(String[] args) {

            System.out.println(tl.get());
            tl.set(666);
            System.out.println(tl.get());
        }
ThreadLocal存取每一个线程共享变量的隔离性

每一个线程的本地变量,之间互相不干预。

package com.example.test;

public class Test250 {
    static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
        protected Integer initialValue() {
            return (int) (Math.random()*6+9);
        };
    };
   public static void main(String[] args) {
       Thread t = new Thread(new MyThread250A(tl));
       t.start();
       for(int i = 0;i<10;i++) {
            System.out.println("main get"+tl.get());
        }
    }
}

class MyThread250A implements Runnable{
    private ThreadLocal<Integer> tl;
    public MyThread250A(ThreadLocal<Integer> tl) {
        super();
        this.tl = tl;
    }



    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i = 0;i<10;i++) {
            System.out.println("A get"+tl.get());
        }

    }
}

ThreadLocal实现原理
 public T get() {
        Thread t = Thread.currentThread();//获取当前运行的线程对象
        ThreadLocalMap map = getMap(t);
        // ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values.ThreadLocalMap 这个类是一个定制的Hash Map 用来存放线程本地地。threadLocals是定义在Thread类中的一个公有属性,需要闯入当前运行的线程,他是ThreadLocalMap 的实例。
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
        //如果该运行的线程对应的ThreadLocalMap 是空也就是threadLocals为空。
    }
//Entry 条目中,将ThreadLocal的实例对象作为键,存入的值作为value,保存到Map中
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
  private T setInitialValue() {
        T value = initialValue();//调用ThreadLocal对象的默认初始化方法,取得值。
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);//存放键值对
        else
            createMap(t, value);//会构造一个ThreadLocalMap,即  t.threadLocals
        return value;
    }
  void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);//存放键值
    }
  • 首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,根据 Thread.currentThread(),拿到该线程的 threadlocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal的对象,value为变量副本(即你要存储的值)。
  • 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
  • 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
一个线程可以存放多个ThreadLocal对象
        ThreadLocal<Long> tl = new ThreadLocal<>();
        ThreadLocal<String> tl2 = new ThreadLocal<>();
        tl.set(Thread.currentThread().getId());
        tl2.set(Thread.currentThread().getName());
        System.out.println(tl.get());
        System.out.println(tl2.get());

我们上面讲述了ThreadLocal实际上是通过每一个正在运行的线程的threadLocals,也就是一个map,其中键是ThreadLocal的实例对象,所以是可以有多个不同的。

这里写链接内容

InheritableThreadLocal

首先该类是继承于ThreadLocal,该类扩展了 ThreadLocal,为子线程提供从父线程那里继承的值:在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。通常,子线程的值与父线程的值是一致的。

 static InheritableThreadLocalTest tt = new InheritableThreadLocalTest();
   public static void main(String[] args) {
    /*MyThread260 mt =  new MyThread260(tt);
    Thread t = new Thread(mt);*/
     //如果放在这里达不到继承父线程的值,因为此时父类的threadLocals 还没有设值哦。
    for(int i=0;i<10;i++) {
        System.out.println("main"+tt.get());
    }
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    MyThread260 mt =  new MyThread260(tt);
    Thread t = new Thread(mt);
    t.start();
  }
}

class MyThread260 implements Runnable{
    private InheritableThreadLocalTest t;
    public MyThread260(InheritableThreadLocalTest t) {
        super();
        this.t = t;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<10;i++) {
            System.out.println("子线程"+t.get());
        }
    }
}

在这里已定义要注意一点,就是你必须在父类线程threadLocals 设置好了,创建子线程,这样才可以继承于父线程的,才可以获取,这样值才相等。

public class InheritableThreadLocalTest extends InheritableThreadLocal{
    @Override
    protected Object initialValue() {
        // TODO Auto-generated method stub
        return new Date().getTime();
    }
}
InheritableThreadLocal的原理

参考学习

 /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

获取线程的私有值,可以通过threadLocals ,子线程要获取父线程的值,通过inheritableThreadLocals ,其实两者差不多。

那它是如何传递值的呢:

关键在于:

Thread thread = new Thread();

我们在线程构造方法中,找到如下:

  if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

当我们在创建一个新的线程的时候,它会首先去检查父线程inheritableThreadLocals ,如果不为空,那么子线程,就拿过来。

 ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;//获取该值。
    }
值继承并修改

重写childValue,会在原来的基础上,将父类的值进行修改。

    @Override
    protected Object childValue(Object parentValue) {
        // TODO Auto-generated method stub
        return super.childValue(parentValue)+"我修改了";
    }
InheritableThreadLocal注意点

在子线程取得父线程的值的时候,子线程就已经确定了 ThreadLocal.ThreadLocalMap inheritableThreadLocals ,而如果你这时在修改inheritableThreadLocals 的值,子线程读到的还是原来的旧值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值