2 baidu 28379135 baidu_28379135 于 2015.07.22 16:28 提问

java中多线程下静态connection的问题

普遍观点是不要使用静态的连接,但是……我面临的需求情况是1.数据只查不改2.该连接只在用户登陆时使用,而其他过程有单独的连接池(与登录不是一个库),用池
觉得浪费3.可能在同一时间有几千人同时登陆,而一天内的其他时间登录次数很少。
以下是部分代码:

 public class CWJdbcConnection {
    private static Map<String, String> map = new GetFile().getFile(
            "FcgDriverClassName", "FcgIp", "FcgName", "FcgPasswd");//获取连接信息
    private static int flag = 0;
    private static Connection conn;
    private PreparedStatement ps = null;
    private boolean isClosed = false;

    static {
        try {
            Class.forName(map.get("fcgDriverClassName"));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public CWJdbcConnection() {
        CWJdbcConnection.doConnection(1);
    }

    public ResultSet doSql(String sql) {
        if (conn == null)
            try {
                conn = DriverManager.getConnection(map.get("fcgIp").toString(),
                        map.get("fcgName").toString(), map.get("fcgPasswd")
                                .toString());
            } catch (SQLException e) {
                e.printStackTrace();
            }
        try {
            ps = conn.prepareStatement(sql);
            return ps.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void close() {
        if (isClosed)
            return;
        if (ps != null)
            try {
                ps.close();
            } catch (SQLException e) {
            }
        isClosed = true;
        CWJdbcConnection.doConnection(-1);
    }

    private synchronized static void doConnection(int a) {
        flag += a;
        if (flag < 0)
            flag = flag / 0;//不要嘲笑我……
        if (flag == 0 && conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            conn = null;
        }
    }
}

使用方法是:

CWJdbcConnection cw = new CWJdbcConnection();
ResultSet   rs = cw.doSql(sql);
doRS(rs);//进行操作
cw.close();

我在这里向大家请教的是:这个类在并发的情况下会出现什么问题?
PS:在手头没有测试工具和不熟悉程序测试的情况下,我仅仅是开了100个线程,让它们并发读数据,暂时没发现问题,线程全部结束后Connection也都关闭了。但是我不放心啊!!

4个回答

danielinbiti
danielinbiti   Ds   Rxr 2015.07.22 16:37
已采纳
 public ResultSet doSql(String sql) {
        if (conn == null)
            try {//这里并发的时候可能会进入不止1次,对conn初始化进行加锁,这单例初始化很常见,一搜一堆。
                conn = DriverManager.getConnection(map.get("fcgIp").toString(),
                        map.get("fcgName").toString(), map.get("fcgPasswd")
                                .toString());
            } catch (SQLException e) {
                e.printStackTrace();
            }
        try {
            ps = conn.prepareStatement(sql);
            return ps.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
danielinbiti
danielinbiti 回复baidu_28379135: 严谨的态度比较好,注意微观吧。我这用词也不一定都是术语,只能说把一件事过程描述一下。
2 年多之前 回复
baidu_28379135
baidu_28379135 我采纳不代表我认可,特此声明。
2 年多之前 回复
baidu_28379135
baidu_28379135 回复danielinbiti: 嗯。我找到“物理连接”这个词语的意思了。“当程序执行 SqlConnection.Close 后,物理连接并不会被立即释放”……我承认自己学的是简化版的JDBC。
2 年多之前 回复
baidu_28379135
baidu_28379135 我和同事研究了半天“指向”这个词,共识是指向是指“引用指向内存”。话说“物理连接”是什么?是PreparedStatement ?我觉得如果不是我学的JDBC是简化版,就是我和您学的不是一个JDBC。
2 年多之前 回复
danielinbiti
danielinbiti 回复baidu_28379135: A与数据库建立了物理连接,你不关闭,怎么断开与物理连接。不是说没有指向A,物理连接不存在了。正相反,物理连接存在,但你已经找不到手工关闭这个物理连接的变量(也就是开关了)
2 年多之前 回复
baidu_28379135
baidu_28379135 回复danielinbiti: 依照您的意思,既然没有变量指向A,那我为什么要关闭呢?只要所有Connection都关了不就行了?(其实那个“2个”和“指向”不知道是什么意思)。很抱歉这个解释我不能接受啊!啊!啊!如果没有其他人的解答我会采纳您,请允许我等待一阵子。
2 年多之前 回复
danielinbiti
danielinbiti 回复baidu_28379135: 静态指同一个类实例中都是一个值,但不意味着静态变量值不能变。因为java自动回收的,所以这很少考虑内存。如果有帮助,有时间顺手采纳一下。
2 年多之前 回复
danielinbiti
danielinbiti 回复baidu_28379135: conn是一个,但你初始化时是生成了2个,conn先指向A,再指向B,最终conn是B,也就是conn.close()是关闭b,那A怎么关闭呢,没有变量指向A了
2 年多之前 回复
baidu_28379135
baidu_28379135 A和B的conn不是一样的么?静态的。如果不是一个那单例就没意义了。
2 年多之前 回复
danielinbiti
danielinbiti 回复baidu_28379135: conn = DriverManager.getConnection就这段代码,加入A用户进来用了,申请了一个连接A,这时B用户并发进来了,这时候也申请了一个连接B,最终conn指向连接B,也就是连接A在程序里是取不到了。
2 年多之前 回复
baidu_28379135
baidu_28379135 没有理解为什么会出现空闲的connection和为什么会有没有close的。??
2 年多之前 回复
danielinbiti
danielinbiti 回复baidu_28379135: 因为你这里是只读,也不牵涉修改之类的。也不会出现问题。除非高并发,一下子把你的连接池耗光了。顶多上面代码出现空闲的connect,因为如果并发最终conn指向变了,并发时候生成的没有close掉。
2 年多之前 回复
baidu_28379135
baidu_28379135 慢的原因是在最开始conn为null时,设置了100个线程同时申请,于是华丽的堵住了……(该需求需要考虑同时初始化申请的情况)
2 年多之前 回复
baidu_28379135
baidu_28379135 谢!使用了双重锁,并且给conn加了volatile,于是只初始化一次了……但是好慢!!有其他方法吗?又及:代码在别的地方有问题吗?
2 年多之前 回复
danielinbiti
danielinbiti 回复baidu_28379135: http://blog.csdn.net/xubo578/article/details/6571660这是几种修改方式的优缺点
2 年多之前 回复
baidu_28379135
baidu_28379135 确实是这样。我竟然犯了这种错误…………加锁是不行的,是不是有其他方法呢?
2 年多之前 回复
baidu_28379135
baidu_28379135   2015.07.22 16:40

确实是这样。我竟然犯了这种错误…………
加锁是不行的,是不是有其他方法呢?

oyljerry
oyljerry   Ds   Rxr 2015.07.22 16:50

单独搞一个数据库连接类,它内部用数据库连接池等,其他线程有数据库查询等,都把数据放到一个队列等,有这个数据库类统一处理,并返回结果等。这样就不需要每个线程都进行数据库操作

baidu_28379135
baidu_28379135 回复oyljerry: 这位同志,请问您真的看了我的提问吗?虽然“提问”里有个“提”,但不是“题目”的“题”。(好吧,我理解太长不看。)
2 年多之前 回复
oyljerry
oyljerry 回复baidu_28379135: 就是你不要频繁open,close数据库连接,而是交给统一的处理,它一直打开,直到程序退出再close等、
2 年多之前 回复
baidu_28379135
baidu_28379135 实话是完全看不懂在说什么……
2 年多之前 回复
sina_2831808769
sina_2831808769   Rxr 2015.07.25 13:54

public ResultSet doSql(String sql) {
if (conn == null)
try {//这里并发的时候可能会进入不止1次,对conn初始化进行加锁,这单例初始化很常见,一搜一堆。
conn = DriverManager.getConnection(map.get("fcgIp").toString(),
map.get("fcgName").toString(), map.get("fcgPasswd")
.toString());
} catch (SQLException e) {
e.printStackTrace();
}
try {
ps = conn.prepareStatement(sql);
return ps.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}

Csdn user default icon
上传中...
上传图片
插入图片