ThreadLocal 线程变量问题? 400 报错
曾经发贴 请教 C3P0 意外关闭的问题, 本以为是 C3P0的问题, 今换成 DRUID 依然 存在, 后经多次测试发现一下问题:
两个Action : A B
两个Service: A B, A B中使用的数据库连接都是 从ThreadLoal中获取, 在Action 结束后自自行关闭数据库, 让其返回数据池控制
现有一 页面 中同时存在 两个请求A,B.
A从数据库中获取一 TREE 菜单, 使用递归操作, 会经过多次的数据查询, 速度较慢, 结束后关闭连接
B从数据库中获取一 系统标识变量, 速度较快且从ThreadLoal中获取为同一个Connection, 结束后关闭连接.
此时 多次测试会发现, A中connetion 不时被关闭, 经查询测试 为B Action请求结束后关闭导致.
不知以上分析是否正确, 如果将 TREE菜单的节点拉长,增加查询时间, 此种状况出现几率较高
可否有好的解决方法 避免此中准概况
所有的连接池管理的连接,用完之后要立刻关闭,不能通过ThreadLocal等方式缓存!######
Action是 A B,Service也是A B,看的好晕。
获取为同一个Connection!我觉得不是增加查询时间的问题,怎么能让不同的数据服务而且是没有事务关联的,使用同一个Connection呢?
怀疑是ThreadLoal没有使用好,根据servlet规范,2个不同的action不在一个线程环境里的,怎么B action能断开A action的连接?
我将代码和测试结果贴一下, 大家帮我分析一下
public final synchronized Connection getConnection() { Connection conn = conns.get(); System.out.println("threadLocal====="+conn); try { if (conn == null || conn.isClosed()) { conn = getConnection(dataSource); } } catch (SQLException e) { } System.out.println("newConn====="+conn); conns.set(conn); return (show_sql && !Proxy.isProxyClass(conn.getClass())) ? new _DebugConnection().bindConnection(conn) : conn; } public final void closeConnection() { Connection conn = conns.get(); System.out.println("CloseConn====="+conn); try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { } conns.remove(); }以上为活得连接和关闭连接的代码,
页面一次请求时正常日志如下:
就是说每次请求时,ThreadLocal都是应该为NULL,之后开启关闭同一个连接, 此时正常
threadLocal=====null
newConn=====com.mysql.jdbc.JDBC4Connection@163b102
[2013-01-18 10:29:23][47ms]WEBFRAME(tom.control.LoginControl.getLeftMenu)->IP:127.0.0.1
CloseConn=====com.mysql.jdbc.JDBC4Connection@163b102
多个请求时日志如下:
此时发现, 有个连接是直接从ThreadLoca中获取,并不是重新获取数据池的连接, 并在两次关闭时, 第一次关闭成功, 第二次关闭时已为NULL
threadLocal=====null
newConn=====com.mysql.jdbc.JDBC4Connection@163b102
threadLocal=====com.mysql.jdbc.JDBC4Connection@163b102
newConn=====com.mysql.jdbc.JDBC4Connection@163b102
forward========com.mysql.jdbc.JDBC4Connection@163b102
CloseConn=====com.mysql.jdbc.JDBC4Connection@163b102
[2013-01-18 10:31:55][16ms]VMCONTROL(VelocityServlet.parse:datagrid.htm)->IP:127.0.0.1
[2013-01-18 10:31:55][16ms]WEBFRAME(tom.control.LoginControl.forward)->IP:127.0.0.1
CloseConn=====null
private final static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
就是这样, 还有就是只定义一个servlet , setvlet 中post 和 get 方法分别 new Control 方法, 上面的两个connection对象 分别是tom.control.LoginControl.getLeftMenu,tom.control.LoginControl.forward,同一个control中的两个方法, 调试时经过两次 service方法的, 但是同一个control对象, 按照servlet规范 按理是两个不同的线程
######
测来测去, 上面两个action 中获取的 conn连接是在同一个连接, 第二次取得的为ThreadLocal.get() 中的, 水平有限
######回复 @nubo : 使用ThreadLcoal主要是为了 统一关闭conn,避免conn被遗忘关闭, 关闭需要做转移,这样好控制, 以前也没用这个######Threadlocal在某些web服务器不能正常使用的原因是:那个web服务器会在一个httprequest使用完之后,重新使用处理这个request的线程做下一次处理,如果你remove的不及时就出错了,而且根据你的需求好像完全不需要使用到threadlocal,配一个数据源,简单方便。######tomcat吗,tomcat用线程池,一般情况线程是重复利用、不关闭的,先后两次请求是同一个线程对象,threadlocal缓存里也是同一个对象,第一次关闭了第二次又使用它就出错了。版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。