您的位置:首页 > 编程语言 > Java开发

Java——ThreadLocal类

2016-05-10 09:40 423 查看

一,引入ThreadLocal

/*测试ThreadLocal对象
* ThreadLocal:这个类提供了一个线程本地的变量。
* 这些变量在被共享访问的情况下在不同的线程里是独立的 ( 必须通过 get 和 set 方法来访问 ) 。
* 很显然该类提供了一个机制可以防止多线程访问带来的不安全机制。实际上就是在线程本地保存一个变量,
* 而不是通过共享变量。这个就要看我们的使用场合了,如果我们确实需要共享的数据,那还是必须通过同步机制来保证数据的安全。
* 如果有些情况希望不同的线程保存的变量各自分开,那用这个还是比较合适的。

ThreadLocal 这个类本身不是代表线程要访问的变量,这个类的成员变量才是。
JDK1.5 给 ThreadLocal 加了泛型功能,即是 ThreadLocal<T>, 这个泛型 T 即是要线程的本地变量。
线程通过 ThreadLocal 的 get 和 set 方法去访问这个变量 T 。 ThreadLocal 提供了一个机制,
它能保证线程通过这个来访问它来访问类型为 T 的变量的时候是不同的拷贝。所以访问该变量必须通过 Threadlocal
这个类只提供了两个 public 方法,即是 get() 和 set ()方法来访问。

同时还提供了一个 inintValue() 的 protected 方法。该方法用来初始化变量值。

注意 :默认情况下 initValue(), 返回 null 。线程在没有调用 set 之前,第一次调用 get 的时候,
get 方法会默认去调用 initValue 这个方法。所以如果没有覆写这个方法,可能导致 get 返回的是 null 。
当然如果调用过 set 就不会有这种情况了。但是往往在多线程情况下我们不能保证每个线程的在调用 get 之前都调用了 set ,
所以最好对 initValue 进行覆写,以免导致空指针异常。

* */

       测试,在没有ThreadLocal的时候:

public class TestThreadLocal {
public static int a = 0;

public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
for(int i=0;i<5;i++){

System.out.println(Thread.currentThread().getName()+":"+a);

}
}

public static class MyThread extends Thread {
public void run(){
for(int i=0;i<5;i++){
a=++a;

System.out.println(Thread.currentThread().getName()+":"+a);

}
}
}

}

  测试结果:

main:1
main:2
main:3
main:4
main:5
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
Thread-0:10
   发现结果很混乱,两个线程的执行明显有交叉。

   使用ThreadLocal之后:

public class TestThreadLocal {
//public static int a = 0;
public static ThreadLocal<Integer> a=new ThreadLocal<Integer>(){
public Integer initialValue(){//初始化a的值
return 0;
}
};

public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
for(int i=0;i<5;i++){
//a=++a;
a.set(a.get()+1);
System.out.println(Thread.currentThread().getName()+":"+a.get());

}
}

public static class MyThread extends Thread {
public void run(){
for(int i=0;i<5;i++){
//a=++a;
a.set(a.get()+1);
System.out.println(Thread.currentThread().getName()+":"+a.get());

}
}
}

}


结果:

Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
main:1
main:2
main:3
main:4
main:5
  使用了线程本地变量之后,我们必须通过get和set方法对变量进行读写。

二,简单介绍ThreadLocal

     

            /**
* Returns the current thread's "initial value" for this
* thread-local variable. This method will be invoked the first
* time a thread accesses the variable with the {@link #get}
* method, unless the thread previously invoked the {@link #set}
* method, in which case the <tt>initialValue</tt> method will not
* be invoked for the thread. Normally, this method is invoked at
* most once per thread, but it may be invoked again in case of
* subsequent invocations of {@link #remove} followed by {@link #get}.
*
* <p>This implementation simply returns <tt>null</tt>; if the
* programmer desires thread-local variables to have an initial
* value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
* subclassed, and this method overridden. Typically, an
* anonymous inner class will be used.
*
* @return the initial value for this thread-local
*/
protected T initialValue() {
return null;
}
    

        变量的初始化操作在这里完成,默认返回null,建议override此方法,确保get之前已进行过set操作,防止get的时候出错。

    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();
}
      在ThreadLocal类中,有一个内部静态 类ThreadLocalMap,在这里存放着以key为当前threadLocal的object。

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}


     set方法也是这样类似的,ThreadLocal里面通常放入的值通常就是采用这两种方法进行操作的,哦,还有remove:

  public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
   好多代码のの。。。。大致先瞅瞅,然后接着切换回我们的spring源码解析,这篇只是小 插曲。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: