ThreadLocal的应用案例详解

ThreadLocal的应用场景
ThreadLocal是解决线程安全问题的一个较好的方案,它通过为每个线程提供一个独立的本地值去解决并发访问的冲突问题。在很多情况下,使用ThreadLocal比直接使用同步机制(如synchronized)解决线程安全问题更简单、更方便,且结果程序拥有更高的并发性。

1 线程隔离

ThreadLocal的主要价值在于线程隔离,ThreadLocal中的数据只属于当前线程,其本地值对别的线程是不可见的,在多线程环境下,可以防止自己的变量被其他线程篡改。另外,由于各个线程之间的数据相互隔离,避免了同步加锁带来的性能损失,大大提升了并发性的性能

在“线程隔离”场景中,使用ThreadLocal的典型案例为:可以为每个线程绑定一个数据库连接,使得这个数据库连接为线程所独享,从而避免数据库连接被混用而导致操作异常问题。

下面的代码来自Hibernate,代码中通过ThreadLocal进行数据库连接(Session)的“线程本地化”存储,主要的代码如下:

private static final ThreadLocal threadSession= new ThreadLocal();
public static Session getSession(){
    Session s = (Session)threadSession.get();
    if(s==null){
        getSessionFactory().openSession();
        threadSession.set(s);
    }
}

Hibernate对数据库连接进行了封装,一个Session代表一个数据库连接。通过以上代码可以看到,在Hibernate的getSession()方法中,首先判断当前线程中有没有放进去Session,如果还没有,那么通过sessionFactory().openSession()来创建一个Session,再将Session设置到ThreadLocal变量中,这个Session相当于线程的私有变量,而不是所有线程共用的,显然其他线程中是取不到这个Session的。

一般来说,完成数据库操作之后程序会将Session关闭,从而节省数据库连接资源。如果Session的使用方式为共享而不是独占,在这种情况下,Session是多线程共享使用的,如果某个线程使用完成之后直接将Session关闭,其他线程在操作Session时就会报错。所以Hibernate通过ThreadLocal非常简单地实现了数据库连接的安全使用。

2 跨函数传递

数据通常用于同一个线程内,跨类、跨方法传递数据时,如果不用ThreadLocal,那么相互之间的数据传递势必要靠返回值和参数,这样无形之中增加了这些类或者方法之间的耦合度。由于ThreadLocal的特性,同一线程在某些地方进行设置,在随后的任意地方都可以获取到。线程执行过程中所执行到的函数都能读写ThreadLocal变量的线程本地值,从而可以方便地实现跨函数的数据传递。使用ThreadLocal保存函数之间需要传递的数据,在需要的地方直接获取,也能避免通过参数传递数据带来的高耦合。

在“跨函数传递数据”场景中使用ThreadLocal的典型案例为:可以为每个线程绑定一个Session(用户会话)信息,这样一个线程所有调用到的代码都可以非常方便地访问这个本地会话,而不需要通过参数传递。

ThreadLocal在“跨函数数据传递”场景的典型应用有很多:
(1)用来传递请求过程中的用户ID。
(2)用来传递请求过程中的用户会话(Session)。
(3)用来传递HTTP的用户请求实例HttpRequest。
(4)其他需要在函数之间频繁传递的数据。

public class Test {
    // session id,线程本地变量
    private static final ThreadLocal<String> sidLocal= new ThreadLocal("sidLocal");

    // 用户信息,线程本地变量
    private static final ThreadLocal<UserDTO> sessionUserLocal = new ThreadLocal<>("sessionUserLocal");

    // session,线程本地变量
    private static final ThreadLocal<HttpSession> sessionLocal = new ThreadLocal<>("sessionLocal");

    // 保存session在线程本地变量中
    public static Session setSession(HttpSession httpSession){
        sessionLocal.set(httpSession);
    }

    // 取得绑定在线程本地变量中的session
    public static HttpSession getSession(){
        HttpSession session = sessionLocal.get();
        Assert.notNull(session,"session 未设置");
        return session;
    }
    
    // 省略....
}

你可能感兴趣的:(多线程开发,java)