ThreadLocal的使用场景(在spring中的使用)

 

置顶 义臻 2016-08-11 14:11:32  2732  收藏 2
分类专栏: Spring 应届生求职
版权
经历了几天的研究,终于是明白了ThreadLocal在Spring事务管理过程中发挥的用途。下面就以图文的形式和大家分享,如有错误,欢迎指正。

大家都知道,Spring允许以声明的方式进行事务管理。通过声明的方式,程序员可以仅仅专注于业务代码,事务管理由Spring框架代为进行。

以JDBC为例,正常的事务代码可能如下:

dbc = new DataBaseConnection();//第1行
Connection con = dbc.getConnection();//第2行
con.setAutoCommit(false);// //第3行
con.executeUpdate(...);//第4行
con.executeUpdate(...);//第5行
con.executeUpdate(...);//第6行
con.commit();第7行

上述代码,可以分成三个部分:


事务准备阶段:第1~3行
业务处理阶段:第4~6行
事务提交阶段:第7行


在Spring框架中,程序员专注于设计业务处理阶段,事务准备阶段和事务提交阶段由Spring来完成。在实际开发过程中,我们仅仅编写了业务处理阶段,事务准备阶段和事务提交阶段会由Spring框架根据我们的事务相关配置文件动态生成--利用AOP。关于AOP,这里就不说了,网上有很多资料。

但是大家需要注意一个问题,在利用AOP动态生成的代码中,如何才能让三个阶段使用同一个数据源连接呢?这是很重要的。如果三个阶段使用不同的数据源连接,自然是错误的。

现在需要办到的是 让软件结构中纵向的三个阶段 使用同样的一个参数,而这三个阶段之间不可以进行参数传递。解决方案是---线程绑定。

Web容器中,每个完整的请求周期会由一个线程来处理。因此,如果我们能将一些参数绑定到线程的话,就可以实现在软件架构中跨层次的参数共享(是隐式的共享)。这是一件很牛逼的事情,在框架中被经常使用。而JAVA中恰好提供了绑定的方法--使用ThreadLocal。

ThreadLocal是一种线程本地变量,使用ThreadLocal的形式声明一个变量,该变量就会在每个线程中创建一个变量的副本。

 

 

 

public class Demo {
    public static ThreadLocal<String> threadLocalString = new ThreadLocal<String>(){
        protected String initialValue() {
            return "";
        }
    };
    public static ThreadLocal<Long> threadLocalLong =new ThreadLocal<Long>(){
        protected Long initialValue() {
            return 0L;
        }
    };
    public static void main(String [] args){
        threadLocalLong.set(100L);
        threadLocalString.set("test");
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocalString.set("thread");
                System.out.println(threadLocalLong.get());
                System.out.println(threadLocalString.get());
            }
        }).start();
        
        System.out.println(threadLocalLong.get());
        System.out.println(threadLocalString.get());
    }
}
 

从上面的代码可看出,在不同的线程中调用同一个类对象的get()方法,输出依据线程的不同而不同。
再来看一个关于ThreadLocal的例子:

 

 

import java.util.HashMap;
import java.util.Map;
 
public class Demo {
    public static void main(String [] args){
        ResourceHolder.putResource("conn",new Conn("connection1"));
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 该线程不会得到主线程绑定的变量
                System.out.println(ResourceHolder.getResource("conn"));
            }
        }).start();
        
        System.out.println(ResourceHolder.getResource("conn"));
        new Demo().function1();
        new Demo().function2();
        System.out.println(ResourceHolder.getResource("conn"));
    }
    public void function1(){
        System.out.println(ResourceHolder.getResource("conn"));
    }
    public void function2(){
        System.out.println(ResourceHolder.getResource("conn"));
    }
}
 
class ResourceHolder{
    
    public static ThreadLocal<Map<Object,Object>> threadLocalMap=new ThreadLocal<Map<Object,Object>>();
    public static void putResource(Object key,Object value){
        if(threadLocalMap.get()==null)
            threadLocalMap.set(new HashMap<Object,Object>());
        threadLocalMap.get().put(key, value);
    }
    public static Object getResource(Object key){
        if(threadLocalMap.get()==null)
            threadLocalMap.set(new HashMap<Object,Object>());
        return threadLocalMap.get().get(key);
    }
    public static void clearResource(Object key,Object value){
        if(threadLocalMap.get()!=null)
            threadLocalMap.remove();
    }
}
class Conn{
    private String name;
    
    public Conn(String name) {
        super();
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Conn [name=" + name + "]";
    }
}
 


现在我们可以考虑使用ThreadLocal来将 事务准备阶段使用的连接 绑定到当前线程 以便在之后的 业务处理阶段 和 事务提交阶段使用了。不过问题来了,这个ThreadLocal放在哪里呢?一种方案是写到DataSource中,但DataSource是策略模式动态配置的,况且都是第三方的,不那么容易改。

我们再一次想到AOP,为DataSource创建一个代理类,每次调用DataSource的getConn方法的时候,都由拦截器拦截并转换为对DataSource代理类的调用,在代理类中加一些猫腻;

看代码: (代理类)

 

package com.xyz.transaction;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class DataSourceHandler implements InvocationHandler {
    
    private Object originalDataDource;
    public Object bind(Object obj) {
        this.originalDataDource=obj;
        return Proxy.newProxyInstance(this.originalDataDource.getClass().getClassLoader(),
                originalDataDource.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // TODO Auto-generated method stub
        if("getConn".equals(method.getName())){//默认数据源的获取连接方法为getConn()
            if(ResourceHolder.getResource(proxy)==null){
                Object obj=method.invoke(originalDataDource, args);
                ResourceHolder.addResource(proxy, obj);
            }
            return ResourceHolder.getResource(proxy);
        }else{
            return method.invoke(originalDataDource, args);
        }
    }
    
}
 

package com.xyz.transaction;
 
import java.util.HashMap;
import java.util.Map;
 
public class ResourceHolder {
    private static ThreadLocal<Map<Object,Object>> threadLocalMap=new ThreadLocal<Map<Object,Object>>();
    public static void addResource(Object key,Object value){
        if(threadLocalMap.get()==null)
            threadLocalMap.set(new HashMap<Object, Object>());
        threadLocalMap.get().put(key, value);
    }
    public static Object getResource(Object key){
        if(threadLocalMap.get()==null)
            threadLocalMap.set(new HashMap<Object, Object>());
        return threadLocalMap.get().get(key);
    }
    public static void clear(){
        threadLocalMap.remove();
    }
}


来看以上代码,每次访问getConn方法的时候,都查看是否在当前线程绑定的Map中有对应的连接,如果有直接返回。如果没有,再向真实的getConn请求获得一个连接并放到当前线程绑定的Map中。

 

 

流程如图所示,图片代码见附件。

 

 

到知乎阅读最新的技术博客:https://www.zhihu.com/people/hulianwangzhaopin/activities

笔者开设了一个讲述学习java路线的知乎live,欢迎收听https://www.zhihu.com/lives/932192204248682496

提供给想学习云计算的同学,欢迎收听https://www.zhihu.com/lives/1046567982750281728

广而告之,欢迎转发!!!

团队简介:用户对阿里云OpenAPI的每一次调用,都需要我们团队产品的深度参与,包括身份产品、认证产品、权限产品、审计产品、资源管理产品、网关产品、SDK产品、API生态等等。团队高速发展中,有意者知乎上私信我 @义臻

 

阿里云开放平台-企业IT治理团队招聘研发JAVA、C++都要,根据面试情况定级P6,P7,P8等你来拿。

 


                                                                    

 

 

 

SimpleConnectionPool.zip (39.9 KB)

下载次数: 5


大小: 1.2 MB
查看图片附件
————————————————
版权声明:本文为CSDN博主「义臻」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yizhenn/article/details/52384520

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值