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() { } }