浅析 ThreadLocal

Posted 坚守一辈子的幸福

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析 ThreadLocal相关的知识,希望对你有一定的参考价值。

一、ThreadLocal类说明

  ThreadLocal,很容易让人望文生义,直译"本地线程"。ThreadLocal不是一个thread,是thread的局部变量。使用ThreadLocal维护变量时,它为每个使用它的线程提供独立的线程副本,每个线程可以改变自己的副本,而不会影响到其它线程对应的副本。

  从线程的角度看,目标变量就象是线程的本地变量,这也是类名中"Local"所要表达的意思。

二、ThreadLocal常用方法

①、当前变量的初始值
  protected T initialValue() 

源码 如下,该方法是一个protected方法,显然是为了让子类覆盖而设计的。该方法是在get|set会调用,且只会被调一次。ThreadLocal缺省实现直接返回null

protected T initialValue() {
        return null;
    }

②、设置当前线程的线程局部变量值

  void set(T value)

③、返回当前线程的线程局部变量值

  public T get()

④、删除当前线程的线程局部变量值,节省内存的占用。

  public void remove()

三、下面我们通过一个具体例子,看看ThreadLocal的具体用法---Id生成器

id生成器

public interface Sequence {

    public int getId();
}

public class LocalSequenceImpl implements Sequence {

    private ThreadLocal<Integer> container = new ThreadLocal<Integer>() {
        
        @Override
        protected Integer initialValue() {
            return 0;
        };
    };
    
    @Override
    public int getId() {
        container.set(container.get() + 1);
        return container.get();
    }

}

线程

public class ThreadLocalDemo extends Thread {

    private static Sequence obj;

    @SuppressWarnings("static-access")
    public ThreadLocalDemo(Sequence obj) {
        this.obj = obj;
    }

    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + obj.getId());
        }
    }

    public static void main(String[] args) {
        
        Sequence obj = new LocalSequenceImpl();
        
        test(obj);

    }

    static void test(Sequence obj) {

        ThreadLocalDemo thread1 = new ThreadLocalDemo(obj);

        ThreadLocalDemo thread2 = new ThreadLocalDemo(obj);

        ThreadLocalDemo thread3 = new ThreadLocalDemo(obj);

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

结果  : 

Thread-0 : 1
Thread-1 : 1
Thread-1 : 2
Thread-2 : 1
Thread-2 : 2
Thread-2 : 3
Thread-1 : 3
Thread-0 : 2
Thread-0 : 3

从上述结果,每个线程只会更改自己的局部变量,并没有发生互相干扰的情况。可以看出来,ThreadLocal为使用它的线程提供独立的副本。

不信?我们下面再通过一个不使用ThreadLocal来验证下。

public class SequenceImpl implements Sequence {
    
    private static int id = 0;

    @Override
    public int getId() {
        id = id + 1;
        return id;
    }
}

 

 结果

Thread-0 : 2
Thread-0 : 4
Thread-0 : 5
Thread-2 : 3
Thread-2 : 6
Thread-2 : 7
Thread-1 : 1
Thread-1 : 8
Thread-1 : 9

 

 仔细观察下结果,三个线程同时访问一个局部变量id,发生了互相干扰的情况。

 

四、拓展

看下ThreadLocal源码,底层是用Map实现的。我们可以自己设计一个ThreadLocal

public class MyThreadLocal<T> {

    private Map<Thread, T> container = Collections.synchronizedMap(new HashMap<>());
    
    public void set(T value) {
        container.put(Thread.currentThread(), value);
    }
    
    public T get() {
        Thread currentThread = Thread.currentThread();
        T value = container.get(Thread.currentThread());
        if(value == null && !container.containsKey(currentThread)) {
            value = initialValue();
            container.put(Thread.currentThread(), value);
        }
        return value;
    }
    
    protected T initialValue() {
        return null;
    }
}

 

以上是关于浅析 ThreadLocal的主要内容,如果未能解决你的问题,请参考以下文章

浅析 ThreadLocal

源码浅析—ThreadLocal

源码浅析—ThreadLocal

浅析 Java ThreadLocal

源码浅析ThreadLocal类

阿里架构师浅析ThreadLocal源码——黄金分割数的使用