Java线程synchronized失效问题:连接池获取连接异常求助
让我来帮你排查一下代码里的问题,主要有几个关键点导致了synchronized失效和连接获取异常:
问题分析
1. 锁对象错误导致同步彻底失效
看AbstractD3Dao里的getCurrentSession()方法:
synchronized (conexao.getInstance()) { while (connPool == null) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } connPool = conexao.getInstance(); } }
这里有两个致命问题:
- 你锁的是
conexao.getInstance()返回的MVConnection实例,但每次调用getInstance()都会返回新的可用连接(或等待后返回),这意味着不同线程锁的是完全不同的对象!没有共享锁的情况下,synchronized自然起不到互斥作用,这就是你觉得它"失效"的核心原因。 - 调用
wait()时没有指定对象,实际是对AbstractD3Dao的实例调用wait(),但你当前持有的锁是MVConnection对象的锁,这会触发非法监视器状态异常(即使没抛出,逻辑上也完全混乱),因为wait()必须在持有对应对象锁的前提下调用。
2. 等待/通知机制完全不匹配
getInstance()里的notify()是对ConexaoD3实例调用的,但getCurrentSession()里的wait()是对AbstractD3Dao实例调用的,两者根本不在同一个锁对象的等待队列里,线程永远不会被正确唤醒。
3. 连接查找的死循环浪费资源
getInstance()里用ConcurrentHashMap.search()做并行搜索,找不到可用连接时会进入无限循环空转,严重浪费CPU资源。
修复方案
1. 统一锁对象与等待/通知逻辑
修改getCurrentSession(),确保锁的是固定的ConexaoD3实例,且等待/通知的对象一致:
protected MVConnection getCurrentSession() { System.out.println("Abstract request the conn"); MVConnection connPool = null; // 锁ConexaoD3实例本身,而非返回的连接对象 synchronized (conexao) { while (connPool == null) { try { System.out.println("Aguardando Conexao"); System.out.println(Thread.currentThread().getName() + " is waiting"); // 对conexao实例调用wait(),因为我们持有的是它的锁 conexao.wait(); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); // 恢复中断状态,避免线程状态异常 } connPool = conexao.getInstance(); } } try { System.out.println(" Abstract assineg the PIB " + connPool.getServerPort() + "to use"); } catch (MVException e) { e.printStackTrace(); } return connPool; }
2. 修正getInstance()的同步与通知逻辑
优化连接查找逻辑,确保通知的是同一个锁对象的等待线程:
public synchronized MVConnection getInstance() { System.out.println("pass here on getInstance"); System.out.println("--------------------------------------------------------"); System.out.println("Before request Instance"); LogLinhas(); System.out.println("--------------------------------------------------------"); MVConnection searchResult = null; System.out.println(Thread.currentThread().getName() + " is running"); // 改用普通遍历查找可用连接,避免并行搜索的不必要开销 for (Map.Entry<MVConnection, Boolean> entry : listaConn.entrySet()) { if (entry.getValue()) { searchResult = entry.getKey(); break; } } // 没有可用连接直接返回null,让调用方等待(避免死循环空转) if (searchResult == null) { return null; } // 更新连接状态为忙碌 listaConn.replace(searchResult, false); try { System.out.println("Set " + searchResult.getServerPort() + " as busy"); } catch (MVException e) { e.printStackTrace(); } // 用notifyAll()唤醒所有等待线程,避免遗漏 notifyAll(); System.out.println(Thread.currentThread().getName() + " notifying"); System.out.println("--------------------------------------------------------"); System.out.println("After request Instance"); LogLinhas(); System.out.println("--------------------------------------------------------"); return searchResult; }
3. 补充连接释放逻辑(必须)
你现在只实现了连接获取,没有释放逻辑,会导致所有连接被标记为忙碌后,线程永远无法获取连接。添加释放方法:
public synchronized void releaseConnection(MVConnection conn) { if (conn != null && listaConn.containsKey(conn)) { listaConn.replace(conn, true); try { System.out.println("Set " + conn.getServerPort() + " as free"); } catch (MVException e) { e.printStackTrace(); } notifyAll(); // 通知等待线程有新的可用连接 } }
记得在使用完连接后调用conexao.releaseConnection(conn)释放资源。
4. 可选:用更专业的工具类实现连接池
ConcurrentHashMap并不是连接池的最佳选择,Java自带的BlockingQueue天生支持阻塞等待可用资源,能大幅简化代码:
private BlockingQueue<MVConnection> connectionQueue; // 初始化时将所有可用连接放入队列 public void initConnections(List<MVConnection> connections) { connectionQueue = new ArrayBlockingQueue<>(connections.size(), true); connectionQueue.addAll(connections); } public MVConnection getInstance() throws InterruptedException { // take()会自动阻塞直到有可用连接 return connectionQueue.take(); } public void releaseConnection(MVConnection conn) { if (conn != null) { connectionQueue.offer(conn); } }
这种方式完全不需要手动处理同步和等待/通知,更可靠也更简洁。
内容的提问来源于stack exchange,提问作者euobeto
相关产品推荐
相关产品推荐

