zoukankan      html  css  js  c++  java
  • ThreadLocal类

    概述

    线程容器,给当前线程绑定一个 Object 内容,以后只要线程不变,可以随时取出。其底层实际是类似HashMap一样的东西,但是它的key是根据当前线程自己确定的,而值则需要我们自己进行设置。这样在一个线程内就可以任意使用。这样可以确保线程安全。

    示例:改变线程,无法取出内容。

    final ThreadLocal<String> threadLocal = new ThreadLocal<>();
    threadLocal.set("测试");
    new Thread(){
    	public void run() {
            //在匿名线程类中使用外部的变量,外部变量只能是final修饰,这样可以保证在该线程使用时外部变量不会发生变化
    		String result = threadLocal.get();
            //结果无法取出
    		System.out.println("结果:"+result);
    	};
    }.start();
    

    ThreadLocal在MyBatis中的应用

    在未和Spring整合时,MyBatis使用比较麻烦,我们可以通过封装工具类的方式来简化代码。

    public class MyBatisUtil {
        private static SqlSessionFactory factory = null;
        private static ThreadLocal<SqlSession> sessionThreadLocal = new ThreadLocal<>();
        static{
            try {
                InputStream stream = Resources.getResourceAsStream("mybatis.xml");
                factory = new SqlSessionFactoryBuilder().build(stream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static SqlSession getSession(){
            SqlSession sqlSession = sessionThreadLocal.get();
            if (sqlSession == null){
                sqlSession = factory.openSession();
                sessionThreadLocal.set(sqlSession);
            }
            return sessionThreadLocal.get();
        }
    
        public static void closeSession(SqlSession session){
            if (session != null){
                session.close();
            }
            sessionThreadLocal.remove();
        }
    }
    

    在这里ThreadLocal保证线程安全的作用还不太明显,但是如下所示

    /**
     * 处理数据库连接的类,同时封装了对事务的处理
     */
    public class JDBCUtils {
        //数据库连接池C3P0
       private static ComboPooledDataSource dataSource=new ComboPooledDataSource();
       //用来处理多线程并发处理问题(并发问题的出现是因为共享了成员变量的原因)
       private static ThreadLocal<Connection> tl=new ThreadLocal<>();
        /**
         * 通过C3p0数据库连接池获取数据库连接Connection
         * @return Connection
         */
        public static Connection getConnection(){
            Connection conn=tl.get();
            try {
                if (conn!=null) return conn;
                return dataSource.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 获取ComboPooledDataSource
         * @return ComboPooledDataSource
         */
        public static ComboPooledDataSource getDataSource(){
            return dataSource;
        }
    
        /**
         * 开启事务
         */
        public static void beginTransaction() throws SQLException {
            Connection conn = tl.get();
            if (conn!=null) throw new SQLException("已经开启事务,请勿重复开启!");
            conn=dataSource.getConnection();
            conn.setAutoCommit(false);
            tl.set(conn);
        }
    
        /**
         * 提交事务
         */
        public static void commitTransaction() throws SQLException {
            Connection conn = tl.get();
            if (conn!=null){
                conn.commit();
                conn.close();
                conn=null;
                tl.remove();
            }else{
                throw new SQLException("事务还未开启,无法提交!");
            }
        }
        /**
         * 回滚事务
         */
        public static void rollbackTransaction() throws SQLException {
            Connection conn=tl.get();
            if (conn!=null){
                conn.rollback();
                conn.close();
                conn=null;
                tl.remove();
            }else{
                throw new SQLException("事务还未开始,无法回滚!");
            }
        }
        /**
         * 关闭连接,释放资源
         * @param connection Connection
         */
        public static void releaseConnection(Connection connection) throws SQLException {
            Connection conn=tl.get();
            if (conn==null){
                connection.close();
            }else if (connection!=conn){
                connection.close();
            }
            tl.remove();
        }
    }
    

    如果我们在上面代码中不使用ThreadLocal类就有可能会产生线程安全问题。例如在某一个线程开启事务后,Connection对象就被实例化了,而且由于是static修饰,所以是类变量,那么当另一个线程访问时,因为Connection不是null,就会将第一个访问者的Connection对象返回给这个线程,这样,两个线程就在共享一个变量了,这样是非常危险的事情。而使用了ThreadLocal后就比较安全了,因为ThreadLocal类为每一个线程维护唯一的Object变量。所以每一个线程互不干扰。

    但是在MyBatis中,ThreadLocal还有其它的用途。那就是通过Filter来完成dao层和service层代码的分离。

    @WebFilter(urlPatterns = "/*")
    public class MyBatisFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            SqlSession session = MyBatisUtil.getSession();
            try{
                chain.doFilter(request,response);
            }catch (Exception e){
                session.rollback();
            }finally {
                MyBatisUtil.closeSession(session);
            }
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
  • 相关阅读:
    js面对对象和jQuery的使用
    感知器PLA算法
    MSP430G2系列学习笔记
    数学建模——规划问题
    51单片机学习(一)
    打靶法求解两点边值问题
    GAOT工具箱(遗传算法)的应用
    数学建模方法
    关于mysql 5.7 版本登录时出现错误 1045的随笔
    oracle复习(二)
  • 原文地址:https://www.cnblogs.com/zwscode/p/14284062.html
Copyright © 2011-2022 走看看