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(); } } }
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>
.