初见 ThreadLocal 类
这个类能够将一个对象和一个线程绑定起来,我的理解就是他维持了一个Map<Thread, T>集合。
之所以写这个类是因为 DBUtils 工具类,在 JavaEE 经典三层结构中对于事务的操作,不方便放在 DAO 层,因为具有侵入性,只适合放在 Service层开启事务,但是由于要调用 DAO 中不同的方法来完成一个事务,就涉及到 conn 对象的传递,因为要保证是同一个 conn 对象在操作事务,要不然就会出乱子了,就会想到将 conn 对象当成参数传递给 ADO 层的若干方法,但是有时候连 Service 都没有 conn 对象的持有,更别说当成参数传递给 DAO 层了,但是想一想,执行一个事务的线程肯定是同一个线程,这样就可以用上ThreadLocal 类了,在使用连接池时就将 conn 对象和当前线程绑定,在之后的 ADO 中获取 conn 对象时,也是通过连接池获取 conn 对象,此时也肯定是同一个 conn 对象。
这个ThreadLocal类中提供的方法不多,我下面使用的就是 get/set 方法
下面是一个转账的事务:(数据库中 account 表有三个字段:id, name, money)
下面是JDBCUtils工具类中核心代码:
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource(); private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); // 获得连接: public static Connection getConnection() throws SQLException{ Connection conn = tl.get(); if(conn == null){ conn = dataSource.getConnection(); tl.set(conn); } return conn; } // 开启事务的方法: public static void beginTransaction() throws SQLException{ Connection conn = tl.get(); if(conn == null){ conn = dataSource.getConnection(); tl.set(conn); } conn.setAutoCommit(false); } // 事务提交的方法: public static void commitTransaction() throws SQLException{ Connection conn = tl.get(); conn.commit(); } // 事务回滚的方法; public static void rollbackTransction() throws SQLException{ Connection conn = tl.get(); conn.rollback(); }
Service中的测试代码:
/** * @param from :转出账号 * @param to :转入账号 * @param money :转账金额 */ public void transfer(String from,String to,Double money){ AccountDao accountDao = new AccountDao(); try { JDBCUtils.beginTransaction(); accountDao.out(from, money); accountDao.in(to, money); JDBCUtils.commitTransaction(); } catch (SQLException e) { try { JDBCUtils.rollbackTransction(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); } } @Test public void demo1(){ AccountServicce accountServicce = new AccountServicce(); accountServicce.transfer("aaa", "bbb", 1000d); }
ADO 中完成事务中两个部分的方法:
public void out(String from,Double money){ Connection conn = null; PreparedStatement stmt = null; try{ conn = JDBCUtils.getConnection(); String sql = "update account set money = money - ? where name = ?"; stmt = conn.prepareStatement(sql); stmt.setDouble(1, money); stmt.setString(2, from); stmt.executeUpdate(); }catch(Exception e){ e.printStackTrace(); } } public void in(String to,Double money){ Connection conn = null; PreparedStatement stmt = null; try{ conn = JDBCUtils.getConnection(); String sql = "update account set money = money + ? where name = ?"; stmt = conn.prepareStatement(sql); stmt.setDouble(1, money); stmt.setString(2, to); stmt.executeUpdate(); }catch(Exception e){ e.printStackTrace(); } }
用过 DBUtils 的博友肯定知道,他里面也是使用了两种方式,一种是绑定到线程(绑定过程是由连接池完成的),一种是传递 conn 对象,前者适用于非事务类的 sql 操作,后者适合事务 sql 操作(因为事务需要自己手动开启,提交或回滚,需要在 Server 层获取到 conn 对象)。
这个类在 struts2 也有用到,在绑定动作 action对象所在线程和其数据集合对象 ActionContext就会用到这个类,这也是 struts2 相比 struts1 线程安全的原因了;这里的 ActionContext 也就是 contextMap 了,ognl 表达式的数据中心。
在获取当前 action(也是当前线程) 的 Actioncontext 对象时使用的是下面的方法:
总是不够完善,希望有人能指出我的错误。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?