ThreadLocal

什么是ThreadLocal

翻译过来中文意思就叫线程局部变量(thread local variable),就是为每个线程都创建一个这样的变量(以ThreadLocal对象为键、任意对象为值的存储结构),这个变量被附带在线程上,每个线程之接相互隔离,互不干扰,该变量副本只能创建它的线程能使用。

ThreadLocal应用场景

  • 如下代码:
public class DBUtil {

    private static Connection connect = null;

    public static Connection getConnection() {
        if(connect == null){
            connect = DriverManager.getConnection();
        }
        return connect;
    }

    public static void close() {
        if(connect!=null){
            connect.close();
        }
    }
}
  • 观察一下这段代码在单线程中使用是没有任何问题的,但是如果在多线程中使用呢?因为JDBC规范并未要求Connection自身一定是线程安全的,很显然,如果没有额外的协调时,在多线程中使用会存在线程安全问题:

    (1):这里面的2个方法都没有进行同步,在多线程调用的情况下很可能在getConnection方法中会多次创建connect
    (2):由于connect是共享变量,那么必然在调用connect的地方需要使用到同步来保障线程安全,因为很可能一个线程在使用connect进行数据库操作,而另外一个线程调用close方法关闭链接(就可能会报 No operations allowed after connection closed)。

  • 所以出于线程安全的考虑,必须将这段代码的两个方法进行同步处理,并且在调用connect的地方需要进行同步处理,这样将会大大影响程序执行效率,因为一个线程在使用connect进行数据库操作的时候,其他线程只有等待。

  • 还有再试想下这个connect对象到底需不需要同步?不管是谁来getConnection方法都是为了得到一个数据库的连接,其实这种场景是不需要同步的;那么我们既需要保证线程安全又不需要同步的话使用ThreadLocal还是比较合适的,多线程多实例,确保线程封闭性。

改造一下代码:

public class DBUtil {

  private static final ThreadLocal<Connection> conn = new ThreadLocal<>();

    public static Connection getConnection() {
        Connection con = conn.get();
        if (con == null) {
            try {
                Class.forName("com.mysql.jdbc.Driver");
                con = DriverManager.getConnection("url", "root", "root");
                conn.set(con);
            } catch (Exception e) {
                // ...
            }
        }
        return con;
    }


    public static void close() {
        Connection con = conn.get();
        if(con !=null){
            con.close();
        }
    }
}

这样子,每个线程都有自己的数据库连接(当然,正规的应用应该要加连接池限制资源之类的),就不会产生那样的冲突了。还有由于在每个线程中都创建了副本,所以要考虑它对资源的消耗。

写的还比较粗糙,如果我的理解或者代码有不正确的地方,还望指正。

posted on 2018-05-25 22:39  愤怒的苹果ext  阅读(26)  评论(0编辑  收藏  举报

导航