A couple of coding issues:
1) Your ConnectionManager 'singleton' may not be a singleton - its getInstance() method is not properly synchronized, so it could produce multiple ConnectionManagers. I don't see why you need lazy initialization for the ConnectionManager, so if I were you I would instantiate the instance in-place (so you don't have to worry about synchronization and thread safe lazy initialization).
2) Your PoolThread's getConnectionFromList() method may return a null connection if the wait() was interrupted. That is a bad thing. I would put the try/catch inside the while loop so if the wait() is interrupted it goes back to check if list isEmpty() and if it is continues to wait.
3) In the PoolThread's close() method, you catch the IllegalMonitorStateException. This exception is (a) impossible to get in your code as written and (b) indicates an un-recoverable programming error. It should not be caught. When you see that during
testing it means you have to fix the programming error (there is no way to fix it at run time).
4) The standard practice for forced actions, like closing a connection, is to put the action in a finally block. So in your client code I would do:
This allows you to:
- be sure the close always happens
- catch only meaningful exceptions (you aren't forced to catch(Exception e) which could let code continue even in the event when you can't fix the problem, and continuing will mean other problems)
There are also a number of other, style or architecture problems with your code:
1) Your ConnectionManager has a getConnection() method, which actually returns a List<Connection>. This is a bad idea, either getConnection() should return a Connection or the method name should be changed to reflect that you are getting a list of connections.
2) On a related note: your ConnectionManager always generates a List of Connections, the ConnectionManager creates and defines the number of Connections that are made. This is usually the job of the connection pool (in your case the PoolThread) - the ConnectionManager should be used only to create connections, and the PoolThread should be responsible for creating and maintaining the list of connections (including the size of the list.) What I would suggest is the ConnectionManager getConnection() method would return a single connection, and the PoolThread would create a List<Connection> and call ConnectionManager.getConnection() a couple of times to fill the List.
3) Your PoolThread class is poorly named. It is not a Thread (a stream of executing code0, it is an object which holds some connections and distributes them when they are ready. Calling it something more descriptive of what it is (like ConnectionPool) would be best.
4) In PoolThread, the method getConnectionFromList() gives too much away - there is no need to make it known the connections are stored in a List. A simple getConnection() or getConnectionFromPool() would be better (it hides the implementation detail of there being a List involved).