关于ThreadLocal的理解

来源:互联网 发布:手机淘宝生日在哪设置 编辑:程序博客网 时间:2024/05/18 00:38

  当访问共享的可变数据时,通常需要使用同步。一种避免使用同步的方法就是不共享数据(虽然有点儿蠢...)。如果仅仅在单线程内访问数据,就不需要同步,这种技术就称作线程封闭(Thread Confinement)。

  维持线程封闭性的一种较为规范的方法是使用ThreadLocal,这个类可以使得线程中的某个值与保存值的对象关联起来。ThreadLocal类提供了一系列的方法,这些方法为每个使用该变量的线程都存有一份独立的副本。

  自jdk1.2之后就提供了java.lang.ThreadLocal这个类。该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其getset 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

  例如,以下类生成对每个线程唯一的局部标识符。 线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。

  import java.util.concurrent.atomic.AtomicInteger; public class UniqueThreadIdGenerator {     private static final AtomicInteger uniqueId = new AtomicInteger(0);     private static final ThreadLocal < Integer > uniqueNum =          new ThreadLocal < Integer > () {             @Override protected Integer initialValue() {                 return uniqueId.getAndIncrement();         }     };      public static int getCurrentThreadId() {         return uniqueId.get();     } } // UniqueThreadIdGenerator

  每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

该类中的方法(不包括继承的方法):

initialValue

protected T initialValue()
返回此线程局部变量的当前线程的“初始值”。线程第一次使用get()方法访问变量时将调用此方法,但如果线程之前调用了set(T)方法,则不会对该线程再调用initialValue方法。通常,此方法对每个线程最多调用一次,但如果在调用get()后又调用了remove(),则可能再次调用此方法。

该实现返回 null;如果程序员希望线程局部变量具有 null 以外的值,则必须为 ThreadLocal 创建子类,并重写此方法。通常将使用匿名内部类完成此操作。

返回:
返回此线程局部变量的初始值

get
public T get()
返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用initialValue()方法返回的值。

返回:
此线程局部变量的当前线程的值

set
public void set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。大部分子类不需要重写此方法,它们只依靠initialValue()方法来设置线程局部变量的值。

参数:
value - 存储在此线程局部变量的当前线程副本中的值。

remove
public void remove()
移除此线程局部变量当前线程的值。如果此线程局部变量随后被当前线程读取,且这期间当前线程没有设置其值,则将调用其initialValue()方法重新初始化其值。这将导致在当前线程多次调用initialValue 方法。


         值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版

本的API方法分别是void set(T value)、T get()以及T initialValue()。


ThreadLocal与同步机制的比较

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

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

  而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

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

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


0 0
原创粉丝点击