ThreadLocal详解

转载 2015年07月09日 11:22:30

     转自:http://xhmj12.iteye.com/blog/2215985

 Java中的ThreadLocal类早在JDK1.2中就有了,ThreadLocal为解决多线程并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

 

        ThreadLocal很容易让人望文生义,想当然地认为是这是一个"本地线程"。其实ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易理解些。

 

        ThreadLocal类的方法很简单,只有四个方法:

        1.void set(Object value);

 

        2.public void remove()

               将当前线程的局部变量的值删除,目的是为了减少内存的占用,该方法是JDK1.5新增的方法。需要指出的是,当前线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显示调用该方法消除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

 

        3.protected Object initialValue()

                返回该线程局部变量的初始值,该方法是一个protected方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用的方法,在线程第1次调用get()或者set(Object)才执行,并且仅执行1次。ThreadLocal中缺省实现直接返回一个null。

 

        4.public Object get()

 

        在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

 

        ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中的元素的键为线程对象。而值对应线程的变量副本。

 

        ThreadLocal的原理

        在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:

        

Java代码  收藏代码
  1. public class ThreadLocal  
  2.   
  3. private Map values = Collections.synchronizedMap(new HashMap());  
  4.   
  5. public Object get(){  
  6.         Thread curThread = Thread.currentThread();  
  7.         Object o = values.get(curThread);  
  8.         if(o==null&&!values.containsKey(curThread)){  
  9.                  o = initialValue();  
  10.                  values.put(curThread,o);  
  11.         }  
  12.         values.put(Thread.currentThread(),newValue);  
  13.         return o;  
  14. }  
  15.   
  16. public Object initialValue(){  
  17.          return null;  
  18. }  
  19.   
  20. .....  
  21.   
  22. }  
 

 

     实例

     下面,通过一个具体的实例来看下ThreadLocal的具体使用方法。

      

Java代码  收藏代码
  1. class SequenceNumber {  
  2.   
  3.     // 通过匿名内部类覆盖ThreadLocalde的initialValue()方法,指定初始值  
  4.     private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {  
  5.   
  6.         @Override  
  7.         public Integer initialValue() {  
  8.             return 0;  
  9.         }  
  10.     };  
  11.   
  12.     // 获取下一个序列值  
  13.     public int getNextNum() {  
  14.         threadLocal.set(threadLocal.get() + 1);  
  15.         return threadLocal.get();  
  16.     }  
  17. }  
  18.   
  19. class CreateSNTask implements Runnable {  
  20.   
  21.     private SequenceNumber sn;  
  22.   
  23.     public CreateSNTask(SequenceNumber sn) {  
  24.         this.sn = sn;  
  25.     }  
  26.   
  27.     @Override  
  28.     public void run() {  
  29.         for (int i = 0; i < 3; i++) {  
  30.             System.out.println("Thread[" + Thread.currentThread().getName() + "]sn["  
  31.                     + sn.getNextNum() + "]");  
  32.         }  
  33.     }  
  34.   
  35. }  
  36.   
  37. public class ThreadLocalTest {  
  38.   
  39.     public static void main(String[] args) {  
  40.         SequenceNumber sn = new SequenceNumber();  
  41.         // 3个线程共享sn,各自产生序列号  
  42.         CreateSNTask t1 = new CreateSNTask(sn);  
  43.         CreateSNTask t2 = new CreateSNTask(sn);  
  44.         CreateSNTask t3 = new CreateSNTask(sn);  
  45.         new Thread(t1).start();  
  46.         new Thread(t2).start();  
  47.         new Thread(t3).start();  
  48.     }  
  49.   
  50. }  
         输出结果如下:

 

        

Java代码  收藏代码
  1. Thread[Thread-1]sn[1]  
  2. Thread[Thread-1]sn[2]  
  3. Thread[Thread-1]sn[3]  
  4. Thread[Thread-2]sn[1]  
  5. Thread[Thread-2]sn[2]  
  6. Thread[Thread-2]sn[3]  
  7. Thread[Thread-3]sn[1]  
  8. Thread[Thread-3]sn[2]  
  9. Thread[Thread-3]sn[3]  
     通过输出结果,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

 

 

       ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

       在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题。

        而ThreadLocal则从另一个角度来解决多线程的并发问题。在编写多线程代码时,可以把不安全的变量封装进ThreadLocal里。当然,这样做的前提是,本身的业务逻辑就是这样的:变量对不同的线程是不共享的,本身就是独立的。

       由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK5.0通过泛型很好的解决了这个问题。在一定程度上简化了ThreadLocal的使用,上面的例子就是使用了JDK5.0新的ThreadLocal<T>版本。

       概括起来说,对于多线程资源共享的问题,同步机制采用了"以时间换空间"的方式,而ThreadLocal采用了"以空间换时间"的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

       

       Android中的Handler机制中的Looper类就使用了ThreadLocal。使用ThreadLocal保存各个Handler所在线程使用到的Looper对象。代码如下:

       

Java代码  收藏代码
  1. public final class Looper {  
  2.    private static final String TAG = "Looper";  
  3.   
  4.    // sThreadLocal.get() will return null unless you've called prepare().  
  5.    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  
  6.    private static Looper sMainLooper;  // guarded by Looper.class  
  7.   
  8.    final MessageQueue mQueue;  
  9.    final Thread mThread;  
  10.   
  11.    private Printer mLogging;  
  12.   
  13.     /** Initialize the current thread as a looper. 
  14.      * This gives you a chance to create handlers that then reference 
  15.      * this looper, before actually starting the loop. Be sure to call 
  16.      * {@link #loop()} after calling this method, and end it by calling 
  17.      * {@link #quit()}. 
  18.      */  
  19.    public static void prepare() {  
  20.        prepare(true);  
  21.    }  
  22.   
  23.    private static void prepare(boolean quitAllowed) {  
  24.        if (sThreadLocal.get() != null) {  
  25.            throw new RuntimeException("Only one Looper may be created per thread");  
  26.        }  
  27.        sThreadLocal.set(new Looper(quitAllowed));  
  28.    }  
  29.   
  30.   
  31.    ......  
  32.      
  33.        /** 
  34.     * Return the Looper object associated with the current thread.  Returns 
  35.     * null if the calling thread is not associated with a Looper. 
  36.     */  
  37.    public static Looper myLooper() {  
  38.        return sThreadLocal.get();  
  39.    }  
  40.   
  41.    ......  

 


相关文章推荐

ThreadLocal详解及说明

ThreadLocal详解

  • 2011-07-22 23:51
  • 190KB
  • 下载

ThreadLocal详解

ThreadLocal是什么   早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可...

java多线程模式ThreadLocal原理简述及其使用详解

java多线程模式ThreadLocal原理简述及其使用详解,代码下载地址:http://www.zuidaima.com/share/1781557457128448.htm

Java多线程之详解ThreadLocal类(一)

ThreadLocal类是用来创建和管理线程的本地存储的类。线程的本地存储可以为相同变量的每个不同线程都创建不同的存储,根除了线程对变量的共享,从而防止多线程任务在共享资源上发生冲突。我们先看下列代码...

ThreadLocal详解

ThreadLocal是什么  早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁...

Handler,Loop,Message,MessageQueue,ThreadLocal关系详解

一直对handler机制一知半解,花了两天时间对handler从信息发送到接收,handler机制的整个过程就是handler发送信息给looper,looper在适当的时候通过handler的dis...

详解ThreadLocal模式

ThreadLocal:java.lang.ThreadLocal 作用:解决多线程中数据共享的问题,即:解决了同一线程中隶属于不同开发层次(表示层、业务层、持久层)的数据共享问题,故可以对执行逻辑...
  • A__17
  • A__17
  • 2015-10-04 13:24
  • 355

ThreadLocal 详解

如果在多线程并发环境中,一个可变对象涉及到共享与竞争,那么该可变对象就一定会涉及到线程间同步操作,这是多线程并发问题。 若可变对象将作为线程私有对象,可通过ThreadLocal进行管理,实现线程间...

Android源码解析Handler系列第(二)篇--- ThreadLocal详解

在上篇文章Android源码解析Handler系列第(一)篇说了Message的内部维持的全局池机制。这一篇仍然是准备知识,因为在Handler中有ThreadLocal的身影,大家知道,Handle...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)