ThreadLocal原理剖析


ThreadLocal详解


ThreadLocal在Struts2、spring、hibernate中都有很重要的应用。

ThreadLocal用来解决多线程程序的并发问题

ThreadLocal并不是一个Thread,而是Thread的局部变量

      当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,目标变量就象是线程的本地变量

      这也是类名中“Local”所要表达的意思。

线程局部变量并不是Java的新发明

      Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持。

ThreadLocal类中的方法

      (1)将此线程局部变量的当前线程副本中的值设置为指定值

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

      (2)移除此线程局部变量当前线程的值

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

      (3)返回此线程局部变量的当前线程副本中的值

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

ThreadLocal的原理

      ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

自己模拟ThreadLocal

public class SimpleThreadLocal{
    private Map valueMap=Collections.synchronizedMap(new HashMap());
    public void set(Object newValue){
        valueMap.put(Thread.currentThread(),newValue);//键为线程对象,值为本线程的变量副本
    }
    public Object get(){
        Thread currentThread=Thread.currentThread();
        Object o=valueMap.get(currentThread);//返回本线程对应的变量
        if(o==null&&!valueMap.containsKey(currentThread)){
        //如果在Map中不存在,放到Map中保存起来
            o=initialValue();
            valueMap.put(currentThread,o);
        }
        return o;
    }
    public void remove(){
        valueMap.remove(Thread.currentThread());
    }
    public void initialValue(){
        return null;
    }
}

使用ThreadLocal的具体例子

public class SequenceNumber{
    //通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
    private static ThreadLocal<Integer> seNum=new ThreadLocal<Integer>(){
        protected Integer initialValue(){
            return 0;
        }
    }
    public Integer getNextNum(){
        seNum.set(seNum.get()+1);
        return seNum.get();
    }
    public static void main(String[] args){
        SequenceNumber sn=new SequenceNumber();
        //3个线程共享sn,各自产生序列号
        TestClient t1 = new TestClient(sn);
        TestClient t2 = new TestClient(sn);
        TestClient t3 = new TestClient(sn);
        t1.start();
        t2.start();
        t3.start(); 
    }
    private static class TestClient extends Thread{
        private SequenceNumber sn;
        public TestClient(SequenceNumber sn){
            this.sn=sn;
        }
        public void run(){
            //每个线程打印3个序列号
            for(int i=0;i<3;i++){
                System.out.println("thread["+
                    Thread.currentThread().getName()+",sn["+sn.getNextNum()+"]");
            }
        }
    }
}

threadlocal的应用


处理事务

Struts2中

      当Struts2接收到浏览器发出的url请求时,先经过很多拦截器进行处理,然后执行action动作类中的动作方法,根据动作方法的返回值找到对应的结果视图,返回给页面。

      而这一系列的拦截器以及处理请求业务逻辑的action动作类的动作方法都要使用ValueStack值栈对象,那么如何保证ValueStack值栈的并发问题?

      查看Struts2源码,在ActionContext中,创建了一个ThreadLocal类型的属性,属性名称为actionContext:

      这样一来,相当于在struts2的整个生命周期内(ActionContext类对象,在整个生命周期内有效)不管是拦截器、动作类都可以通过ActionContext.getContext()方法获取放入到ThreadLocal中的ActionContext的实例化对象。从而保证:

ValueStack valueStack = ActionContext.getContext().getValueStack() ;

获取到的是本地线程ThreadLocal的属性actionContext中的valueStack,确保值栈ValueStack的多线程操作的安全问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值