Java并发编程的艺术笔记(四)——ThreadLocal的使用

ThreadLocal,即线程变量,是一个ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。目的就是为了让线程能够有自己的变量

可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。

/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {

        //获取当前线程
        Thread t = Thread.currentThread();
        //得到线程的ThredLocalMap
        ThreadLocalMap map = getMap(t);
        //如果map不为空,则将当前线程的对象作为key,传进来的参数作为value存储
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

看一下ThredLocalMap是什么:

static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        .......

看到这是ThreadLocal的一个内部类,使用Entry类进行存储。K是我们的ThredLocal对象。

总结:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象

再来看下get方法:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

拿到这个entry的value。

ThreadLocal本身并不存值,它只是作为ThreadLocalMap的key,来获取value,因此能实现数据隔离。

注意:由于ThreadLocalMap的生命周期和Thread一样长,因此要手动remove掉对应的key,不然会造成内存泄露。

使用场景:

1.管理Connection,尤其是管理数据库连接。

频繁创建和关闭connection是一件很耗时的操作,因此要用到数据库连接池。ThreadLocal可以很好的管理数据库连接,因为它能够实现当前线程的操作都是用同一个Connection,保证了事务

 

public class ConnectionUtil {
    private static Logger logger = LoggerFactory.getLogger(ConnectionUtil.class);
    //数据库连接池
    private static BasicDataSource dataSource;
    //为不同的线程管理连接
    private static ThreadLocal<Connection> local;
    
    static {
        BufferedReader br = null;
        Properties ipp_prop = new Properties();
        
        try {
            String propertiesurl = System.getProperty("user.dir") + "/ipp_parser.properties";
            br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(propertiesurl)), "utf-8"));
            ipp_prop.load(br);
            br.close();
        }  catch (Exception e1) {
            e1.printStackTrace();
        }
        
        dataSource = new BasicDataSource();
        dataSource.setDriverClassName(ipp_prop.getProperty("db.driver"));
        dataSource.setUrl(ipp_prop.getProperty("db.url"));
        dataSource.setUsername(ipp_prop.getProperty("db.user"));
        dataSource.setPassword(ipp_prop.getProperty("db.password"));
        //初始连接
        dataSource.setInitialSize(Integer.parseInt(ipp_prop.getProperty("db.initsize")));
        //最大连接
        dataSource.setMaxTotal(Integer.parseInt(ipp_prop.getProperty("db.maxtotal")));
        //最长等待时间
        dataSource.setMaxWaitMillis(Integer.parseInt(ipp_prop.getProperty("db.maxwait")));
        //最小空闲
        dataSource.setMinIdle(Integer.parseInt(ipp_prop.getProperty("db.minidle")));
        dataSource.setMaxIdle(Integer.parseInt(ipp_prop.getProperty("db.maxidle")));
        //初始化线程池本地
        local = new ThreadLocal<>();/**得到连接
     * @return
     * @throws SQLException
     */
    public static Connection getOracleConnection() throws SQLException {
        //获取Connection对象
        Connection connection = dataSource.getConnection();
        //把Connection放进local里
        local.set(connection);
        logger.info("get oracleConnection");
        return connection;
    }
    
    public static void closeOracleConnection(){
        Connection connection = local.get();
        
        try {
            if (connection != null) {
                //设置自动提交
                connection.setAutoCommit(true);
                //连接还给连接池
                connection.close();
                local.remove();
                logger.info("close oracleConnection");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/lingluo2017/p/10239081.html
今日推荐