zoukankan      html  css  js  c++  java
  • java—ThreadLocal模式与OSIV模式(53)

           ThreadLocal: 维护线程局部的变量。

           ThreadLocal 不是线程。它就是一个Map。可以保存对象。

           它保存的对象,只与当前线程相关。

           当一个线程还没有运行完成时,如果不想传递数据,可以通过ThreadLocal来保存与这个Thread相关数据。

    用ThreadLocal保存和获取数据的示例:

    public class BaseDemo {
        public static void main(String[] args) {
            //声明Map<Object key,Object value>
            //Object是值,key是当前线程的引用=Thread.currentThread();
            ThreadLocal<Object> tl = new ThreadLocal<Object>();
            //保存数据
            tl.set("Helllo");
            //获取数据
            Object val = tl.get();
            System.err.println(val);
        }
    }

    当多个线程共同访问同一个资源时,用threadLocal来维护某个线程的变量:

    一个应用项目中,一般只要有一个(static)threadlocal的实例就可以了:

    public class MyThreadLocal {
        //声明一个唯一的ThreadLocal
        private static ThreadLocal<Object> tl = new ThreadLocal<Object>(); 
        public static Object getObject(){
            //先从tl中读取数据
            Object o = tl.get();//  如果没有保存过,map.get(Thread.currentThread());
            if(o==null){
                //生成一个随机
                o = new Random().nextInt(100);
                //放到tl
                tl.set(o); 
            }
            return o;
        }
        public static void remove(){
            tl.remove(); 
        }
    }

    对ThreadLocal内部保存的对象来说。你可以执行remove(无数)方法删除与当前thread相关的对象。也可以不执行:

    因为:threadlocal内部使用的是弱引用:

    WeakReferences

    用ThreadLocal管理事务

    用三层模式:

           Serlvet(MVC-C) – Sevice(服务层) – dao(数据访问层)

           写两个dao,在service中调用这两个dao。

           让第一个dao成功。让第二个dao失败,必须都回滚。

    第一步:开发两个dao

    public class UserDao2 {
        public void save(){
            String sql = "insert into users values(?,?,?)";
            QueryRunner run = new QueryRunner();
            try {
                run.update(DataSourceUtils.getConn(),sql,"U002","Jack","333");
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            
        }
    }

    第二步:开发Service

    public class UserService {
        //声明两个dao
        private UserDao1 dao1 = new UserDao1();
        private UserDao2 dao2 = new UserDao2();
         public void save(){
            dao1.save();
            dao2.save();
        }
    }

    第三步:实现一个Servlet

    public class UserServlet extends HttpServlet {
        //声明service的实例
        private UserService service = new UserService();
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            service.save();
        }
    }

    第四步:修改datasourceutils.java

    package cn.hx.utils;
    import java.sql.Connection;
    import javax.sql.DataSource;
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    public class DataSourceUtils {
        // 声明线程局部的容器
        private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); 
        private static DataSource ds;
        static {
            ds = // 默认的读取c3p0-config.xml中默认配置
            new ComboPooledDataSource("itcast");
        }
        public static DataSource getDatasSource() {
            return ds;
        }
        public static Connection getConn() {
            // 先从tl这个容器中获取一次数据,如果当前线程已经保存过connection则直接返回这个connecton
            Connection con = tl.get(); 
            if (con == null) {
                try {
                    con = ds.getConnection();// 每一次从ds中获取一个新的连接
                    //将这个con放到tl中
                    tl.set(con); 
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return con;
        }
    }

    第五步:声明一个过虑器在过虑器开始事务

    package cn.hx.filter;
    
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    import cn.itcast.utils.DataSourceUtils;
    
    public class TxFilter implements Filter{
        public void init(FilterConfig filterConfig) throws ServletException {
        }
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            //获取连接
            Connection con = null;
            //在try中开始事务
            try{
                con = DataSourceUtils.getConn();
                //开始事务
                con.setAutoCommit(false);
                //放行
                chain.doFilter(request, response);
                //如果没有出错。
                con.commit();
            }catch(Exception e){
                System.err.println("出错了");
                try {
                    con.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                throw new RuntimeException(e);
            }finally{
                try {
                    con.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        public void destroy() {
        }
    }

    第六步:将过虑器配置到weeb.xml中。且对某个路径设置过虑

    <filter>
      <filter-name>tx</filter-name>
      <filter-class>cn.itcast.filter.TxFilter</filter-class>
     </filter>
     <filter-mapping>
      <filter-name>tx</filter-name>
         <url-pattern>/tx/*</url-pattern> 
     </filter-mapping>

    第七步:总结

           在过虑器开始事务,就叫一种模式:OSIV模式》

           OSIV – Open Session In View =- 打开与数据库的会话在View层。- Hibernate.—AOP

    第八步:优化:

    在datasourceutls.java实现一个删除thredlocal中与线程相关的对象:

    package cn.hx.utils;
    import java.sql.Connection;
    import javax.sql.DataSource;
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    public class DataSourceUtils {
        // 声明线程局部的容器
        private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
        private static DataSource ds;
        static {
            ds = // 默认的读取c3p0-config.xml中默认配置
            new ComboPooledDataSource("itcast");
        }
        public static DataSource getDatasSource() {
            return ds;
        }
        public static Connection getConn() {
            // 先从tl这个容器中获取一次数据,如果当前线程已经保存过connection则直接返回这个connecton
            Connection con = tl.get();
            if (con == null) {
                try {
                    con = ds.getConnection();// 每一次从ds中获取一个新的连接
                    //将这个con放到tl中
                    tl.set(con);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return con;
        }
        public static void remove(){
            tl.remove();
        }
     }
    在TxFilter中调用一个remove:
    public class TxFilter implements Filter{
        public void init(FilterConfig filterConfig) throws ServletException {
        }
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            System.err.println("thread:"+Thread.currentThread().getName());
            //获取连接
            Connection con = null;
            //在try中开始事务
            try{
                con = DataSourceUtils.getConn();
                //开始事务
                con.setAutoCommit(false);
                //放行
                chain.doFilter(request, response);
                //如果没有出错。
                con.commit();
            }catch(Exception e){
                System.err.println("出错了");
                try {
                    if(e instanceof SQLException){
                        con.rollback();
                    }else{
                        con.commit();
                    }
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                throw new RuntimeException(e);
            }finally{
                try {
                    con.close();
                    DataSourceUtils.remove(); 
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        public void destroy() {
        }
    }
  • 相关阅读:
    idea的alt+enter可以从菜单点吗
    idea能用下划线替换红色报错吗?我色弱,用idea简直太痛苦了
    Idea中JDK为1.8,还提示Diamond types are not supported at this language level
    idea中maven项目下载源码的方式的
    kafka删除topic后再创建同名的topic报错(ERROR org.apache.kafka.common.errors.TopicExistsException)
    Linux虚拟机的命令分发工具。
    关于git SSH Key的 生成
    Windows下Nginx+Mysql+Php(wnmp)环境搭建
    HTTP协议详解
    php面向对象中static静态属性和静态方法的调用
  • 原文地址:https://www.cnblogs.com/zhenghongxin/p/4472109.html
Copyright © 2011-2022 走看看