`
zengbin163
  • 浏览: 13969 次
  • 性别: Icon_minigender_1
  • 来自: 南昌
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

ThreadLocal小谈

阅读更多
ThreadLocal 

作者:Jhonney 来源:expert.blogjava.net  发布时间:2007-03-17 19:00:28.86

早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是似乎现在了解它、使用它的朋友还不多。

  ThreadLocal是什么

  ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。

  ThreadLocal的设计

  首先看看ThreadLocal的接口:

  Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值void set(Object value); // 设置当前线程的线程局部变量副本的值

  ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:





protected Object initialValue() { return null; }

  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);
  }
  return o;
 }

 public void set(Object newValue)
 {
  values.put(Thread.currentThread(), newValue);
 }

 public Object initialValue()
 {
  return null;
 }
}

  当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。

  ThreadLocal的使用

  如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号:





public class SerialNum
{
 // The next serial number to be assigned

 private static int nextSerialNum = 0;
 private static ThreadLocal serialNum = new ThreadLocal()
 {
  protected synchronized Object initialValue()
  {
   return new Integer(nextSerialNum++);
  }
 };

 public static int get()
 {
  return ((Integer) (serialNum.get())).intValue();
 }
}

  SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用:



int serial = SerialNum.get();

  即可。

  在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。

  ThreadLocal与其它同步机制的比较

  ThreadLocal和其它同步机制相比有什么优势呢?ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的,使用这种同步机制需要很细致地分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进ThreadLocal,或者把该对象的特定于线程的状态封装进ThreadLocal。

  由于ThreadLocal中可以持有任何类型的对象,所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本(1.5)将模版的引入,新的支持模版参数的ThreadLocal<T>类将从中受益。也可以减少强制类型转换,并将一些错误检查提前到了编译期,将一定程度地简化ThreadLocal的使用。

  总结

  当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。

分享到:
评论

相关推荐

    浅谈Java引用和Threadlocal的那些事

    浅谈Java引用和Threadlocal的那些事 本文主要介绍了Java引用和Threadlocal的知识点,包括Java中的引用类型、Threadlocal的使用等。 Java中的引用类型: Java中有四种引用类型:强引用(Strong Reference)、软...

    浅谈SpringMVC HandlerInterceptor诡异问题排查

    浅谈SpringMVC HandlerInterceptor诡异问题排查 SpringMVC中的HandlerInterceptor是非常重要的组件之一,它可以在请求处理的各个阶段进行干预和修改。本文将主要介绍如何排查SpringMVC HandlerInterceptor中的诡异...

    浅谈JAVA中多线程的实现.zip

    7. ThreadLocal:为每个线程提供独立的变量副本,避免了线程之间的数据冲突。每个线程都拥有自己的ThreadLocal变量,互不影响。 8. 线程通信:wait(), notify(), notifyAll()这些方法用于线程间的通信。但是必须在...

    【Android】 浅谈Handler机制

    Handler机制产生的原因 在谈论一个机制之前,需要了解这个机制设计出来是为了解决...ThreadLocal相关内容已经写了一片博客分析过了:传送门 那么这篇文章就主要聚焦在Handler以及Looper的具体实现上。 Handler /** *

    浅谈.Net下的多线程和并行计算(全集)

    为了解决这个问题,.NET提供了锁(Lock)、Monitor、Mutex、Semaphore等同步机制,以及线程局部存储(ThreadLocal)来确保数据的一致性。 并行计算进一步扩展了多线程的概念,它涉及在多个处理器或核心上同时执行任务...

    浅谈Spring 的Controller 是单例or多例

    3. **利用`ThreadLocal`确保线程安全**:另一种解决线程安全的方法是使用`java.lang.ThreadLocal`。ThreadLocal为每个线程提供了一个独立的变量副本,确保了线程间的隔离性。这样,即使在单例Controller中,也可以...

    多线程面试59题(含答案)_.zip

    再者,Java并发工具类如ExecutorService、Semaphore、CountDownLatch、CyclicBarrier等也是面试常谈。ExecutorService是线程池的实现,可以有效管理线程资源,避免频繁创建和销毁线程;Semaphore用于限制同时访问...

    浅谈利用Spring的AbstractRoutingDataSource解决多数据源的问题

    在这个类中,我们覆盖了`determineCurrentLookupKey()`方法,获取数据源的key是从`DynamicDataSourceContextHolder`类的`ThreadLocal`变量`dataSourceName`获取的。这样,我们可以通过在不同的线程上下文中设置`...

    java面试技巧及笔试汇集

    4. 线程安全:了解并发编程中的volatile、Atomic类和ThreadLocal。 六、网络编程 1. Socket通信:理解TCP和UDP协议,以及Socket和ServerSocket类的使用。 2. HTTP协议:了解HTTP的基本原理,能实现简单的HTTP客户端...

    大公司的Java面试题集

    4. **并发编程**:Java提供了丰富的并发工具类,如synchronized、volatile、ThreadLocal、Lock接口等。面试者应了解并发模型,熟悉线程安全问题,如死锁、活锁、饥饿等,并能分析和解决这些问题。 5. **JVM**:对...

    浅谈Java中随机数的几种实现方式

    为优化性能,可以考虑将`Random`对象静态化,或者使用线程局部变量(`ThreadLocal`)来保存每个线程自己的`Random`实例。 在多线程环境中,`java.util.Random`类是线程安全的,这意味着多个线程可以安全地共享同一...

    android的消息处理机制(图文+源码分析)—Looper/Handler/Message

    由于每个线程只能有一个Looper,所以Looper使用ThreadLocal来确保线程安全。如果尝试在一个已经有Looper的线程中再次调用`prepare()`,系统会抛出异常。 2. 调用`Looper.loop()`,这会启动一个无限循环,从Message...

Global site tag (gtag.js) - Google Analytics