欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识

通向架构师的阶梯(第七天)之 次  来源:宝鼎软件 时间:2018-02-07

原文出处: 袁鸣凯

一、什么是ThreadLocal

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为办理多线程措施的并发问题提供了一种新的思路。利用这个东西类可以很简捷地编写出美妙的多线程措施。

ThreadLocal很容易让人望文生义,想虽然地认为是一个“当地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它定名为ThreadLocalVariable更容易让人领略一些。

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

从线程的角度看,方针变量就象是线程的当地变量,这也是类名中“Local”所要表达的意思。

线程局部变量并不是Java的新发现,昆山软件公司,许多语言(如IBM IBM XL FORTRAN)在语法层面就提供线程局部变量。在Java中没有提供在语言级支持,而是变相地通过ThreadLocal的类提供支持。

所以,在Java中编写线程局部变量的代码相对来说要鸠拙一些,因此造成线程局部变量没有在Java开拓者中获得很好的普及。

ThreadLocal的接口要领

ThreadLocal类接口很简朴,只有4个要领,我们先来相识一下:

  • void set(Object value)
  • 配置当前线程的线程局部变量的值。

  • public Object get()
  • 该要领返回当前线程所对应的线程局部变量。

  • public void remove()
  • 将当前线程局部变量的值删除,目标是为了淘汰内存的占用,该要领是JDK 5.0新增的要领。需要指出的是,当线程竣事后,对应该线程的局部变量将自动被垃圾接纳,所以显式挪用该要领排除线程的局部变量并不是必需的操纵,但它可以加速内存接纳的速度。

  • protected ObjectinitialValue()
  • 返回该线程局部变量的初始值,该要领是一个protected的要领,显然是为了让子类包围而设计的。这个要领是一个延迟挪用要领,在线程第1次挪用get()或set(Object)时才执行,而且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

    值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API要领也相应举办了调解,新版本的API要领别离是void set(T value)、T get()以及T initialValue()。

    通向架构师的路线(第七天)之<a href=劳务调派信息打点系统 漫谈操作 ThreadLocal 改革你的层次的别离" src="/uploads/allimg/c180207/151O4H542050-1HO.jpg" />

     一、来看一个实际案例

    2.1 同一Service要领中挪用多个Dao要领

    通向架构师的路线(第七天)之<a href=劳务调派信息打点系统 漫谈操作 ThreadLocal 改革你的层次的别离" src="/uploads/allimg/c180207/151O4H543440-29533.jpg" />

    可以看到,我们有一个Service要领,在该Service要领中挪用多个Dao要领,所有在该Service要领中的的Dao都处于同一事务中。

    该Service要领竣事后,提交事务;

    该Service要领中有任何错,回滚事务;

    2.2 传统的做法

    来看下面这段伪代码

    Service层代码:

    public void serviceMethod(){
    
    Connection conn=null;
    
    try{
    
    Connection conn=getConnection();
    conn.setAutoCommit(false);
    Dao1 dao1=new Dao1(conn);
    
    dao1.doSomething();
    Dao2 dao2=new Dao2(conn);
    dao2.doSomething();
    Dao3 dao3=new Dao3(conn);
    dao3.doSomething();
            conn.commit();
    
    }catch(Exception e){
    
        try{
        conn.rollback();
    }catch(Exception ex){}
    
    }finally{
    
    try{
    conn.setAutoCommit(true);
    }catch(Exception e){}
    
        try{
        if(conn!=null){
        conn.close();
        conn=null;
    
    }
    
    }catch(Exception e){}
    
    }
    }

    每个Dao层的代码:

    Class Dao1{
    
    private Connection conn=null;
    
    public Dao1(Connection conn){
        this.conn=conn;
    
    }
    
    public void doSomething(){
    
        PreparedStatement pstmt=null;
    
        try{
    
            pstmt=conn.preparedStatement(sql);
            pstmt.execute…
            …
    
    }catch(Exception e){
    
        log.error(e,”Exeception occurred in Dao1.doSomething():”+e.getMessage,e);
    
    }finally{
    
        try{
    
            if(pstmt!=null){
                pstmt.close();
                pstmt=null;
    
    }
        }catch(Exception e){}
    
    }
    }
    }

    假如我一个Service要领有挪用一堆dao要领,先不说这样写首先粉碎了OOP的封装性原则,假如有一个dao多关了一个conn,那就会导致其它的dao获得的conn为null,这种事在这样的写法下由其当你尚有业务逻辑殽杂在一起时很容易产生。

    笔者曾经碰见过2个项目,呈现out of memory可能是connection pool has been leakage,经查代码就是在每个dao中多关可能在service层中漏关,可能是每个dao有本身的conntionconn=getConnection(),然后还跑到Service层里去关这个connection(那关什么,关个P关!)。

    虽然,假如你说你在写法上绝对promise绝对留意这样的问题不会产生,可是我们来看看下面的这种做法,是否会比上面这个写法更好呢?

    2.3 Spring中的做法

    先来看Spring中的写法。

    各人应该都很熟悉Spring中的写法了,来看一下它是怎么办理的。