ThreadLocal的使用

ThreadLocal 的作用,它可以解决多线程的数据安全问题

ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)

ThreadLocal 的特点:

    1、ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程)
    2、每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个
ThreadLocal 对象实例。
    3、每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型
    4、ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。

我们在不使用threadLocal的时候使用数据关联是这样的:

public class ThreadLocal  {
    private static Map<String,Object> data = new Hashtable<String, Object>();
    private static Random random = new Random();

    public static class Task implements Runnable{
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            Integer i = random.nextInt(1000);
            System.out.println("在线程["+name+"]中生成的随机数是"+i);
            data.put(name,i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Object o = data.get(name);
            System.out.println("在线程中["+name+"]快结束时取值的结果是:"+o);



        }
    }


    public static void main(String [] args){
        for (int i = 0; i < 3; i++) {
            new Thread(new Task()).start();
        }
    }

}
手动实现ThreadLocal
public class ThreadLocalOne {
    private static Map<String,Object> data = new Hashtable<String, Object>();
    private static Random random = new Random();

    public static class Task implements Runnable{

        @Override
        public void run() {
            // 在 Run 方法中,随机生成一个变量(线程要关联的数据),然后以当前线程名为 key 保存到 map 中
            Integer i =  random.nextInt();
            // 获取当前线程名
            String name = Thread.currentThread().getName();
            System.out.println("线程["+name+"]生成的随机数是: "+i);
            data.put(name,i);
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 在 Run 方法结束之前,以当前线程名获取出数据并打印。查看是否可以取出操作
            Object o = data.get(name);
            System.out.println("线程["+name+"]快结束时取出关联的数据是"+o);

        }
    }

    public static void main(String[] args) {
        for (int i = 0; i <3 ; i++) {
            new Thread(new Task()).start();
        }
    }
}

上面是我们的手动实现的ThreadLocal类的实现

使用ThreadLocal进行线程关联

public class ThreadLocalOne {
    private static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
    private static Random random = new Random();

    public static class Task implements Runnable{

        @Override
        public void run() {
            // 在 Run 方法中,随机生成一个变量(线程要关联的数据),然后以当前线程名为 key 保存到 map 中
            Integer i =  random.nextInt();
            // 获取当前线程名
            String name = Thread.currentThread().getName();

            threadLocal.set(i);
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 在 Run 方法结束之前,以当前线程名获取出数据并打印。查看是否可以取出操作
            
            Object o = threadLocal.get();
            System.out.println("线程["+name+"]快结束时取出关联的数据是"+o);

        }
    }

    public static void main(String[] args) {
        for (int i = 0; i <3 ; i++) {
            new Thread(new Task()).start();
        }
    }
}

 

使用 Filter 和 ThreadLocal 组合管理事务

使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完

原理分析图:

 Filter 类代码:

public class TransactionFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain
filterChain) throws IOException, ServletException {
    try {
        filterChain.doFilter(servletRequest,servletResponse);
            JdbcUtils.commitAndClose();// 提交事务
        } catch (Exception e) {
            JdbcUtils.rollbackAndClose();//回滚事务
            e.printStackTrace();
        }
    }
}    

web.xml中的配置

<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.atguigu.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<!-- /* 表示当前工程下所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping> 

一定要记得把 BaseServlet 中的异常往外抛给 Filter 过滤器

public abstract class BaseServlet  extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决 post 请求中文乱码问题
        // 一定要在获取请求参数之前调用才有效
        req.setCharacterEncoding("UTF-8");
        String action = req.getParameter("action");
        try {
            // 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象
            Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
            // 调用目标业务 方法
            method.invoke(this,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);//把异常抛给 Filter 过滤器
        }

    }
}

将所有异常都统一交给 Tomcat,让 Tomcat 展示友好的错误信息页面

 在 web.xml 中我们可以通过错误页面配置来进行管理。

    <!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
    <error-page>
        <!--error-code 是错误类型-->
        <error-code>500</error-code>
        <!--location 标签表示。要跳转去的页面路径-->
        <location>/pages/error/error500.jsp</location>
    </error-page>

    <!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
    <error-page>
        <!--error-code 是错误类型-->
        <error-code>404</error-code>
        <!--location 标签表示。要跳转去的页面路径-->
        <location>/pages/error/error404.jsp</location>
    </error-page>

.

原文地址:https://www.cnblogs.com/zhaoyunlong/p/13901110.html