开发者社区> 问答> 正文

ThreadLocal 线程变量问题? 400 报错

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菜单的节点拉长,增加查询时间, 此种状况出现几率较高

可否有好的解决方法 避免此中准概况





展开
收起
爱吃鱼的程序员 2020-05-31 00:01:27 566 0
1 条回答
写回答
取消 提交回答
  • https://developer.aliyun.com/profile/5yerqm5bn5yqg?spm=a2c6h.12873639.0.0.6eae304abcjaIB

    所有的连接池管理的连接,用完之后要立刻关闭,不能通过ThreadLocal等方式缓存!######

    Action是 A B,Service也是A B,看的好晕。

    获取为同一个Connection!我觉得不是增加查询时间的问题,怎么能让不同的数据服务而且是没有事务关联的,使用同一个Connection呢?

    怀疑是ThreadLoal没有使用好,根据servlet规范,2个不同的action不在一个线程环境里的,怎么B action能断开A action的连接


    ######

    引用来自“wenshao”的答案

    所有的连接池管理的连接,用完之后要立刻关闭,不能通过ThreadLocal等方式缓存!
    个人认为 链接丢到ThreadLocal里缓存到没有错误,问题是 关闭后一定要将缓存的清除才行,我怀疑是链接清除出错导致的.
    ######

    我将代码和测试结果贴一下, 大家帮我分析一下


    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

    ######ThreadLocal conns是怎么定义的?######

    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缓存里也是同一个对象,第一次关闭了第二次又使用它就出错了。
    2020-05-31 00:01:28
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
多IO线程优化版 立即下载
低代码开发师(初级)实战教程 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载