`

ThreadLocal详解

    博客分类:
  • Java
阅读更多

        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,用于存储每一个线程的变量的副本。比如下面的示例实现:

        

public class ThreadLocal

private Map values = Collections.synchronizedMap(new HashMap());

public Object get(){
        Thread curThread = Thread.currentThread();
        Object o = values.get(curThread);
        if(o==null&&!values.containsKey(curThread)){
                 o = initialValue();
                 values.put(curThread,o);
        }
        values.put(Thread.currentThread(),newValue);
        return o;
}

public Object initialValue(){
         return null;
}

.....

}
 

 

     实例

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

      

class SequenceNumber {

    // 通过匿名内部类覆盖ThreadLocalde的initialValue()方法,指定初始值
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {

        @Override
        public Integer initialValue() {
            return 0;
        }
    };

    // 获取下一个序列值
    public int getNextNum() {
        threadLocal.set(threadLocal.get() + 1);
        return threadLocal.get();
    }
}

class CreateSNTask implements Runnable {

    private SequenceNumber sn;

    public CreateSNTask(SequenceNumber sn) {
        this.sn = sn;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("Thread[" + Thread.currentThread().getName() + "]sn["
                    + sn.getNextNum() + "]");
        }
    }

}

public class ThreadLocalTest {

    public static void main(String[] args) {
        SequenceNumber sn = new SequenceNumber();
        // 3个线程共享sn,各自产生序列号
        CreateSNTask t1 = new CreateSNTask(sn);
        CreateSNTask t2 = new CreateSNTask(sn);
        CreateSNTask t3 = new CreateSNTask(sn);
        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }

}
         输出结果如下:

 

        

Thread[Thread-1]sn[1]
Thread[Thread-1]sn[2]
Thread[Thread-1]sn[3]
Thread[Thread-2]sn[1]
Thread[Thread-2]sn[2]
Thread[Thread-2]sn[3]
Thread[Thread-3]sn[1]
Thread[Thread-3]sn[2]
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对象。代码如下:

       

 public final class Looper {
    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    private Printer mLogging;

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }


    ......
    
        /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

    ......

 

分享到:
评论

相关推荐

    04、导致JVM内存泄露的ThreadLocal详解-ev

    04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、...

    java中ThreadLocal详解

    ### Java中ThreadLocal详解 #### 一、ThreadLocal概述 在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,...

    Java ThreadLocal详解_动力节点Java学院整理

    Java ThreadLocal详解 ThreadLocal是Java中的一种机制,可以将变量与线程关联起来,使得每个线程都可以拥有自己的变量副本。 ThreadLocal的出现是为了解决多线程编程中的线程安全问题。 从本质上说,ThreadLocal是...

    2、导致JVM内存泄露的ThreadLocal详解

    ### 导致JVM内存泄露的ThreadLocal详解 #### 一、为什么要有ThreadLocal 在多线程编程中,为了避免线程间的数据竞争和保证线程安全性,常常需要使用同步机制如`synchronized`来控制线程对共享资源的访问。然而,...

    java核心知识点学习----多线程间的数据共享和对象独立,ThreadLocal详解.pdf

    2. **ThreadLocal详解**: ThreadLocal是Java提供的一种线程局部变量,每个线程都拥有自己独立的ThreadLocal副本,互不影响。在上述代码中,创建了一个ThreadLocal实例,然后在线程的run()方法中设置值,每个线程...

    ThreadLocal详解.md

    学习ThreadLocal,了解其中的原理,以及学习其中的优点!避免坑点!!

    ThreadLocal详解及说明

    关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. ...

    java线程本地变量ThreadLocal详解

    Java 线程本地变量 ThreadLocal 详解 ThreadLocal 是 Java 中的一个类,提供了线程安全的对象封装,用于解决多线程访问数据的冲突问题。ThreadLocal 的主要目的是为每个线程提供一个变量副本,从而隔离了多个线程...

    Java 并发编程之ThreadLocal详解及实例

    ThreadLocal 是 Java 中一个强大的工具,它允许在多线程环境中为每个线程维护独立的、私有的变量副本。在并发编程中,ThreadLocal 提供了一种线程间数据隔离的解决方案,避免了传统共享变量带来的同步问题。下面将...

    理解ThreadLocal

    ThreadLocal 详解 ThreadLocal 是 Java 中一个非常重要的类,它为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal 并不是一个 Thread,而是 Thread 的局部变量,它为每个使用该变量的线程提供独立的变量...

    ThreadLocal 用法详解.md

    ThreadLocal 用法详解.md

    Java 中ThreadLocal类详解

    Java中的ThreadLocal类是一个强大的工具,它允许程序员创建线程局部变量。这意味着每个线程都有其自己独立的、不可见的变量副本,从而避免了线程间的数据共享问题,简化了多线程环境下的编程复杂性。ThreadLocal并不...

    Android 详解ThreadLocal及InheritableThreadLocal

    ThreadLocal和InheritableThreadLocal是Java中两个非常重要的线程相关的类,它们在Android开发中也有广泛应用。本文将深入解析这两个概念以及它们在Android环境下的工作原理。 **ThreadLocal** 是一个线程局部变量...

    Java资料-详解ThreadLocal

    然而,有时我们希望在类中定义一个变量,让它具有线程局部性,即每个线程都有一份独立的副本,这时就轮到`ThreadLocal`登场了。 `ThreadLocal`在Java中是一个非常重要的工具类,它为每个线程提供了单独的变量副本,...

    ThreadLocal

    ### ThreadLocal核心概念详解 #### 一、ThreadLocal简介与重要性 ThreadLocal是一个非常重要的Java并发工具类,它的核心概念在于为每一个线程提供了一个独立的变量副本,从而避免了线程之间的数据竞争问题。这使得...

    java ThreadLocal使用案例详解

    Java ThreadLocal使用案例详解 Java ThreadLocal是Java语言中的一种机制,用于为每个线程提供一个独立的变量副本,以解决多线程环境下共享变量的线程安全问题。在本文中,我们将详细介绍Java ThreadLocal的使用案例...

    8个案例详解教会你ThreadLocal.docx

    ThreadLocal 是 Java 中用于处理线程局部变量的一个重要工具,它的设计目的是为了解决多线程环境下线程间数据隔离的问题。以下是对标题和描述中所述知识点的详细说明: 1. **与 Synchronized 的区别** - `...

    Synchronized与ThreadLocal

    #### 二、ThreadLocal机制详解 **ThreadLocal** 提供了一种线程本地存储的解决方案,为每个线程创建独立的副本,避免了线程间的共享和争用问题。 - **作用原理:** - ThreadLocal 维护了一个 Map 结构,其中 Key ...

    Java ThreadLocal用法实例详解

    Java ThreadLocal用法实例详解 Java ThreadLocal是Java中的一种线程局部变量机制,用于保存每个线程独有的数据,以避免线程之间的数据共享问题。ThreadLocal的基本使用非常简单,只需要定义一个ThreadLocal变量,...

Global site tag (gtag.js) - Google Analytics