浅析 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;
    }
}
时间: 2024-08-12 12:31:30

浅析 ThreadLocal的相关文章

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

一. 前提 最近接触到的一个项目要兼容新老系统,最终采用了ThreadLocal(实际上用的是InheritableThreadLocal)用于在子线程获取父线程中共享的变量.问题是解决了,但是后来发现对ThreadLocal的理解不够深入,于是顺便把它的源码阅读理解了一遍.在谈到ThreadLocal之前先买个关子,先谈谈黄金分割数.本文在阅读ThreadLocal源码的时候是使用JDK8(1.8.0_181). 二. 黄金分割数与斐波那契数列 首先复习一下斐波那契数列,下面的推导过程来自某搜

ThreadLocal源码浅析

首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各个线程中访问的是不同的对象. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本.通过ThreadLocal.set()将这个新创建

浅析 Java ThreadLocal

1.概念 synchronized这类线程同步的机制可以解决多线程并发问题,在这种解决方案下,多个线程访问到的,都是同一份变量的内容.为了防止在多线程访问的过程中,可能会出现的并发错误.不得不对多个线程的访问进行同步,这样也就意味着,多个线程必须先后对变量的值进行访问或者修改,这是一种以延长访问时间来换取线程安全性的策略. 而ThreadLocal类为每一个线程都维护了自己独有的变量拷贝.每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了,那就没有任何必要对这些线程进行同步,它们也能最大限

Handler原理浅析

    理解Handler的原理首先要搞清楚什么是Looper,在我的上一篇博文中对此有专门的介绍.Looper的作用是开启一个消息循环,从MessageQueue(Message队列,是Looper的成员变量)中循环取出消息处理.一个线程要使用Handler来处理来自其它线程的消息,这个线程必须有且仅有一个Looper对象与之绑定,也可以说一个Looper对象是是与一个线程一一对应的. Hander有一个Looper类型的成员,在Handler的构造函数(new Handler()或者new

Android的消息处理机制——Looper,Handler和Message浅析

题外话: 说来有些惭愧,对于这三者的初步认识居然是在背面试题的时候.那个时候自己接触Android的时间还不长,学习的书籍也就是比较适合入门的<疯狂Android讲义>,当然在学到Handler这一部分的时候,书中也是有提到一些简单示例,后来在工作中需要用到这个MessageQueue的时候才开始真正琢磨了一下这三者的联系.如果想要对这三者好好理解一番,个人还是比较推荐<深入理解Android卷Ⅰ>.以下对这三者之间的恩怨纠葛的介绍和分析也是参考这本书的相关章节,算是一篇读书笔记吧

浅析Android Handle机制

一.Handle的用例: 1.创建handle实例 new handle(); 2.发送信息载体(Message) sendMessage(msg); 3.处理消息 handleMessage(Message msg){}; 二.原理浅析 结合以上的handle调用三部曲,我们将顺藤摸瓜理清Handle.Looper.Message.MessageQueue的逻辑与关系. 1.new Handle():这个操作将生成一个Handle实例,handle实例有三个属性mLooper.mQueue.m

linux进程调度函数浅析(基于3.16-rc4)

众所周知,进程调度使用schedule()函数来完成,下面我们从分析该函数开始,代码如下(kernel/sched/core.c): 1 asmlinkage __visible void __sched schedule(void) 2 { 3 struct task_struct *tsk = current; 4 5 sched_submit_work(tsk); 6 __schedule(); 7 } 8 EXPORT_SYMBOL(schedule); 第3行获取当前进程描述符指针,存

不惑JAVA之JAVA基础 - ThreadLocal

ThreadLocal在数据库连接和session管理下有广泛的应用,了解ThreadLocal对struts.spring等开源代码的理解有很大的帮助. ThreadLocal如果单纯从名字上来看像是"本地线程"这么个意思,只能说这个名字起的确实不太好,很容易让人产生误解,ThreadLocalVariable(线程本地变量)应该是个更好的名字.我们先看一下官方对ThreadLocal的描述: 该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因

ThreadLocal原理分析与代码验证

ThreadLocal提供了线程安全的数据存储和访问方式,利用不带key的get和set方法,居然能做到线程之间隔离,非常神奇. 比如 ThreadLocal<String> threadLocal = new ThreadLocal<>(); in thread 1 //in thread1 treadLocal.set("value1"); ..... //value的值是value1 String value = threadLocal.get(); in