维持线程封闭性--ThreadLocal类

在并发编程中,线程安全性是需要保证的。在实现线程安全性的方式中,线程封闭是最简单的方式之一,线程封闭就是指仅在单线程内访问数据。当某个对象封闭在一个线程中时,这种用法将自动实现线程安全性,即使被封闭的对象本身不是线程安全的。这篇文章讨论的就是跟维持线程封闭相关的一个类:ThreadLocal。

ThreadLocal类位于java.lang包下,作用是能够保存线程局部变量的状态,使得每次访问此变量时都能获得实时的、正确的值。ThreadLocal对象通常用于防止对可变的单例变量或全局变量进行共享。(需要注意的是,跟ThreadLocal关联的只能是对象,不能是基本数据类型。)例如,如果我们打算为每个线程分配一个ID,这个ID应该不能共享,只在对应线程中使用,所以ThreadLocal类就派上了用场。代码如下:

import java.util.concurrent.atomic.AtomicInteger;

 public class ThreadId {
     
     private static final AtomicInteger nextId = new AtomicInteger(0);
     
     private static final ThreadLocal<Integer> threadId =
         new ThreadLocal<Integer>() {
             @Override protected Integer initialValue() {
                 return nextId.getAndIncrement();
         }
     };

     public static int get() {
         return threadId.get();
     }
 }

以上代码很简单,只是ThreadLocal类如何使用的一个示例,接下来详细介绍ThreadLocal的原理(注意:介绍类源码时所在JDK版本为1.8)

首先是底层数据结构,其决定了ThreadLocal为何能保存变量的状态,在内部,这个数据结构为ThreadLocalMap,更准确地说,是在内部的Entry这个类,我们可以类比集合中的Map。源码如下:

static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
...
}

细节问题暂不考虑,可以发现,Entry的构造方法将ThreadLocal对象与需要保存的对象值关联了起来,这就是核心所在。细看源码的话可以发现ThreadLocalMap内创建了Entry数组,其构造行为和HashMap类似,具体情况请看源码。

继续介绍ThreadLocal类的运作原理,首先是方法列表:

get()获得当前线程局部变量的值
initialValue()
返回当前线程局部变量的初始值
remove()
移除线程与变量的关联
set(T value)
设置当前线程局部变量的值

当第一次调用get()方法时,会调用initialValue()方法,默认返回一个空值。所以在使用ThreadLocal时需要将其子类化并重写此方法,创建需要关联的变量,就如同第一个示例程序那样。而初始化的过程,就是建立新的ThreadLocalMap对象,将ThreadLocal对象与变量关联起来。那么,这又是如何跟线程关联起来呢?线程在Java中对应的是Thread类,在它的源码中,有这么一个字段:

public class Thread implements Runnable {
 /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

}

这就很清楚了,我们回到之前的创建过程,实际上创建ThreadLocalMap的过程是这样的:

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocal、Thread、线程局部变量就这么关联了起来。其他的方法语义都很明确,不用过多解释了。


---------------------------------------------------------------------------------------------

以上就是ThreadLocal的基本情况,如果深究的话看源码就知道还是有很多内容的,大家可以看看,这里点到为止。最后我还想说的就是,尽管知道了ThreadLocal类的作用,但实际应用是没这么简单的,比如滥用ThreadLocal的问题等,如何正确的使用还是要在实践中摸索。


参考资料:

《Java并发编程实战》

Java官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值