Java---ThreadLocal的用法与理解实现

java.lang 类 ThreadLocal<T>

我们可以称ThreadLocal为:线程本地变量

官方API是这样介绍的:

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

我们直接new 就可以构造一个 ThreadLocal对象。

它只有4个方法:

 T get()
          返回此线程局部变量的当前线程副本中的值。
protected  T initialValue()
          返回此线程局部变量的当前线程的“初始值”。
 void remove()
          移除此线程局部变量当前线程的值。
 void set(T value)
          将此线程局部变量的当前线程副本中的值设置为指定值。 

initialValue这个方法是一个延迟调用方法(可以理解成给初始值),在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

如果程序员希望线程局部变量具有 null 以外的值,则必须为 ThreadLocal 创建子类,并重写此方法。通常将使用匿名内部类完成此操作。

通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
  private static ThreadLocal t= new ThreadLocal(){
      public Integer initialValue() {
          return 0;
      }
  };

ThreadLocal是这样做到为每一个线程维护变量的:

在ThreadLocal类中有一个Map<Thread,Object>,用于存储每一个线程与线程变量的值,Map中元素的键为线程对象,而值对应线程的变量值。

我们自己就可以写出一个简单的实现版本:

package cn.hncu;

import java.util.HashMap;
import java.util.Map;

public class MyThreadLocle {
    private Map<Thread, Object> map = new HashMap<Thread, Object>();

    public Object get(){
        Thread curThread = Thread.currentThread();
        Object value = map.get(curThread);
        return value;
    }

    public void set(Object obj){
        Thread curThread = Thread.currentThread();
        map.put(curThread, obj);
    }
}

现在我们用一个实例来加深对ThreadLocal的理解:

package cn.hncu;

import java.util.Random;

import org.junit.Test;

public class ThreadLocalDemo {

    private static ThreadLocal<Object> t1 = new ThreadLocal<Object>();

    public static Object getValue(){
        Object o = t1.get();
        //不用给key,因为t1内部会自动获取当前线程的thread对象,并以上作为key到它的池中去取obj
        if(o==null){
            System.out.println("空的");
            Random r = new Random();
            o = r.nextInt(1000);
            t1.set(o);
        }
        return o;
    }

    @Test
    public void test(){
        Object obj = getValue();
        Object obj2 = getValue();//第二次去拿,不是空的了
        System.out.println(obj+","+obj2);
        System.out.println(obj==obj2);//true

        A a = new A();
        Object obj3 = a.getValue();
        System.out.println(obj==obj3);//true

        B b = new B();
        final Object obj4 = b.getValue();
        System.out.println(obj3==obj4);//true
        //由上面的例子可以知道,只要是同一个线程,不管是哪个类,从ThreadLocal中get的对象都是同一个

        System.out.println("---====不同的线程====---");

        new Thread() {
            @Override
            public void run() {
                A a = new A();
                Object obj5 = a.getValue();
                System.out.println("obj5:"+obj5);

                B b = new B();
                Object obj6 = b.getValue();
                System.out.println("obj6:"+obj6);
                System.out.println("obj5=obj6:"+(obj5==obj6));
                System.out.println("obj4=obj5:"+(obj4==obj5));

            }
        }.start();
    }
}

class A{
    public Object getValue(){
        Object obj = ThreadLocalDemo.getValue();
        System.out.println("A:"+obj);
        return obj;
    }
}

class B{
    public Object getValue(){
        Object obj = ThreadLocalDemo.getValue();
        System.out.println("B:"+obj);
        return obj;
    }
}

通过上面那个实例我们可以知道:

各个线程中访问的是不同的对象。

通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。

看下jdk1.7的部分ThreadLocal类源码:

get()与set()方法:

/**
     * Returns the value in the current thread‘s copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread‘s value of this thread-local
     */
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

/**
     * Sets the current thread‘s copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread‘s copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
时间: 2024-09-14 06:09:07

Java---ThreadLocal的用法与理解实现的相关文章

关于ThreadLocal 的用法

ThreadLocal,直译为"线程本地"或"本地线程",如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,我认为应该叫做 ThreadLocalVariable(线程局部变量)才对,真不理解为什么当初 Sun 公司的工程师这样命名. 早在 JDK 1.2 的时代,java.lang.ThreadLocal 就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至今没有得到广泛使用.其实它还是挺有用的,不相信的话,我们一起

(转)java.ByteArrayInputStream与ByteArrayOutputStream再次理解

转载自:java.ByteArrayInputStream与ByteArrayOutputStream再次理解 第一次看到ByteArrayOutputStream的时候是在Nutch的部分源码,后来在涉及IO操作时频频发现这两个类的踪迹,觉得确实是很好用,所以把它们的用法总结一下. ByteArrayOutputStream的用法 以下是JDK中的记载: public class ByteArrayOutputStream  extends OutputStream 此类实现了一个输出流,其中

Java ThreadLocal

背景: 最近项目中需要调用其他业务系统的服务,使用的是Java的RMI机制,但是在调用过程中中间件发生了Token校验问题.而这个问题的根源是每次用户操作,没有去set Token导致的.这个Token是存储在ThreadLocal变量中的,根据servlet的单例多线程原理,使用一个拦截器每次向Thread中写入这个token完美的解决了这个问题. ThreadLocal ThreadLocal是Java lang包里面的一个类,这个类用来提供线程级变量的实现.想要明白ThreadLocal首

Java基础12:深入理解Class类和Object类

Java基础12:深入理解Class类和Object类 Java中Class类及用法 Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,即所谓的RTTI. 这项信息纪录了每个对象所属的类.虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类.Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建. 说白了就是: Class类也是类的一种,只是名字和class关键字高度相似.Java是大小写敏感的语言.

JAVA的continue用法

JAVA的continue用法: public class test{ public static void main(String [] args){  for(int i=0;i<=10;i++){   if(i%2!=0){    continue;      }   System.out.println(i);    } }} 其实很简单,continue就是中断本次循环开始下一次. 结合这段代码就是.当i是奇数时,不打印,直接结束循环,开始下一次. 还一个例子: //测试continu

汉诺塔(-) java modPow 的用法;

汉诺塔(一) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵 天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔.不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金 片:一次只移动一片,不管在哪根针上,小片必须在大片上面.僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消

关于Java ThreadLocal

转自:http://www.appneta.com/blog/introduction-to-javas-threadlocal-storage/ What is ThreadLocal? A simple example As its name suggests, a single instance of ThreadLocal can store different values for each thread independently. Therefore, the value stor

JAVA正则表达式高级用法(分组与捕获)

正则表达式在字符串处理中经常使用,关于正则简单的用法相信有一点程序基础的人都懂得一些,这里就不介绍简单基础了.这里主要讲解一下在JAVA中实现了的正则的高级用法-分组与捕获.对于要重复单个字符,非常简单,直接在字符后卖弄加上限定符即可,例如 a+ 表示匹配1个或一个以上的a,a?表示匹配0个或1个a.这些限定符如下所示: X ?     X ,一次或一次也没有X *     X ,零次或多次X +     X ,一次或多次X { n }     X ,恰好 n 次X { n ,}     X ,

java提高篇(四)_理解java的三大特性之多态 转自 http://cmsblogs.com

多态就是指程序中定义 的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该 引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定.因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让 引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个 运行状态,这就是多态性 一. 向上转型

对JAVA多线程 并发编程的理解

对JAVA多线程并发编程的理解 Java多线程编程关注的焦点主要是对单一资源的并发访问,本文从Java如何实现支持并发访问的角度,浅析对并发编程的理解,也算是对前段时间所学的一个总结. 线程状态转换 Java语言定义了5中线程状态,在任何一个时间点,一个线程只能有且只有其中一种状态,这5中状态分别是: ?  新建(New):创建后尚未启动的线程处于这种状态 ?  运行(Runable):Runable包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程可能正在执行,也有可