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详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、...
详解java底层实现原理,ThreadLocal底层实现的数据结构,为什么不会导致内存泄露
导致JVM内存泄露的ThreadLocal详解 为什么要有ThreadLocal ThreadLocal的使用 实现解析 引发的内存泄漏分析 错误使用ThreadLocal导致 线程不安全分析
java核心知识点学习----多线程间的数据共享和对象独立,ThreadLocal详解.pdf
学习ThreadLocal,了解其中的原理,以及学习其中的优点!避免坑点!!
关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. ...
主要介绍了Java 并发编程之ThreadLocal详解及实例的相关资料,需要的朋友可以参考下
ThreadLocal则为每一个线程提供了一个变量副本,从而隔离了多个线程访问数据的冲突,ThreadLocal提供了线程安全的对象封装,下面我们就来详细了解一下吧
ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,本文会详细的介绍一下,有兴趣的可以了解一下
ThreadLocal 用法详解.md
Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal Java资料—详解ThreadLocal
什么是ThreadLocal?顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突...
Android 详解ThreadLocal及InheritableThreadLocal 概要: 因为在android中经常用到handler来处理异步任务,通常用于接收消息,来操作UIThread,其中提到涉及到的looper对象就是保存在Threadlocal中的,因此研究下...
ThreadLocal、InheritableThreadLocal详解 多线程访问同一个共享变量时,容易出现并发冲突,为了保证线程的安全,一般使用者在访问共享变量时,需要进行适量的同步。而ThreadLocal提供了线程的私有变量,每个线程都...
主要介绍了Java ThreadLocal用法,结合实例形式详细分析了ThreadLocal线程局部变量相关原理、定义与使用方法,需要的朋友可以参考下
主要为大家详细介绍了java ThreadLocal的使用案例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
通常情况下,我们创建的成员变量都是线程不安全的。因为他可能被多个线程同时修改,此变量对于多...而使用ThreadLocal创建的变量只能被当前线程访问,其他线程无法访问和修改。也就是说:将线程公有化变成线程私有化。