zoukankan      html  css  js  c++  java
  • 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();
            }
        }
    }
  • 相关阅读:
    Docker学习笔记之常用的 Docker Compose 配置项
    Docker学习笔记之使用 Docker Compose 管理容器
    qt无法使用终端启动的解决方法
    实践卡尔曼滤波--小球追踪
    高斯分布 笔记
    蒙特卡罗定位(Particle Filter Localization)笔记
    珊格地图笔记
    ubuntu14.04 下安装 gsl 科学计算库
    SLAM学习资料汇总
    矩阵的SVD分解
  • 原文地址:https://www.cnblogs.com/lingluo2017/p/10239081.html
Copyright © 2011-2022 走看看