fred-ye / summary

my blog
43 stars 9 forks source link

[Java] ThreadLocal类 #25

Open fred-ye opened 10 years ago

fred-ye commented 10 years ago

最近在看Universal-Image-Loader源码的时候又碰到了ThreadLocal,依稀记得以前用ThreadLocal写过点东西,于是翻看一下以前的笔记回顾一下,在此处也记录一下。

原理

ThreadLocal,线程局部变量,用来保存与当前线程绑定的一些数据。java规范里面是这么说的。ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread。在类中ThreadLocal的实例通常是private static的 用来与线程绑定,一个线程一个。我们主要是使用里面的get()和set()方法。

ThreadLocal中的方法get()set(T value)方法实际上都是对当前Thread类中的一个Map进行操作。我们可以看到在Thread类中维护着这么一个Map。该Map的类型是ThreadLocal类中的一个静态内部类。

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

应用

根据ThreadLocal是线程独享的这个特性。我们可以利用ThreadLocal中的set(T value)方法将一个与数据库的连接设置进去。那么就可以保证每一个线程一个连接。(数据库连接不能在线程间共享【连接时线程不安全的】,会破坏事务的原子性和隔离性。)

直接上代码:

public class JdbcUtil {
    // 该集合用来保存数据的.
    private static Properties info = new Properties();
    static {
        try {
            InputStream is = JdbcUtil.class
                    .getResourceAsStream("/cfg/config.properties");
            // 用info调用load()方法,可完成读文件,解析字符串等任务,并将内容添加到集合中。
            info.load(is);
            // 用了io流之后,一定要记得关流.
            is.close();
        } catch (Exception e) {
            // 在静态代码块中只能抛出这一个异常。ExceptionInInitializerError(e);
            throw new ExceptionInInitializerError(e);
        }
    }
    /*
     * 注:tl 是final,保证其不会指向其它的对象。ThreadLocal的实例通常都是private static final
     * 一个线程一个数据库连接。
     */
    private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    public static Connection getConnection() throws Exception {
        // 刚开始由于没有设置值,所以拿到的是null. tl.get()返回的是一个对象。
        Connection conn = tl.get();
        if (conn == null) {
            Class.forName(info.getProperty("driver"));
            conn = DriverManager.getConnection(info.getProperty("url"),
                    info.getProperty("username"), info.getProperty("password"));
            // 将连接放到ThreadLocal中。
            tl.set(conn);
        }
        return conn;
    }

    // 释放资源。但是有一点不完整。如果在加一个参数PreparedStatement pstm,那就完善了。
    public static void release(ResultSet rs, Statement stm, Connection conn) {
        if (rs != null)
            try {
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        if (stm != null)
            try {
                stm.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        if (conn != null)
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
    }
    //为了测试方便就把测试方法写在这了。
    public static void main(String args[]) throws Exception {
        System.out.println(getConnection());
    }
}