There are certain situations where we need to do this type of thing in programming
- Initiate a resource - (normally one time)
- Keep using the resource
- In a fault when using the resource, reinitialize resource
- And keep using again
Few examples can be JMX proxy/RMI connection or TCP connection to a legacy system.
1 2 3 4 5 6 7 8 9 10 11 12 13 | import java.io.IOException; public class Connection { public void connect() { // } public void sendMessage(String message) throws IOException { // do send message and throw IOException in case of connection error } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import java.io.IOException; public class Client { private Connection connection; public Client() { initConnection(); } private void initConnection() { connection = new Connection(); connection.connect(); } public void process() { boolean success = false; while (!success) { try { connection.sendMessage("some message"); success = true; } catch (IOException e) { initConnection(); } } } } |
Connection class encapsulate resource management. Client.process() method use connection class to send an message and in case of an error, will reconnect by calling Connection.connect().
One issue can rise here when Client.process() method accessed concurrently which is not so rare in an event driven systems. When multiple threads call Client.process() method at a time where connection is unavailable, each thread will call connection.connect() method. This is not desirable since it can cause concurrent issues.
One solution is to synchronous access to initConnection() method.
Accessing connection object through AtomicReference make sure thread safety when multiple threads modify it.
Now concurrency issues have been dealt with. But practically there will be another problem in a condition of considerable number of threads accesses initConnection() method.
Consider two threads A and B. Thread A will acquire mutex and calling c.connect() method where thread B will wait at line 10. After thread A successfully initiate the connection and set to AtomicReference, thread B will acquire the mutex and do the same thing again. Causing unnecessarily reconnect. This might be noticeable specially as the concurrent access increases.
As a solution another Semaphore is included to check the status of the mutex - to check whether someone have already inside the mutex. tryAcquire() method can be used in this case. If another thread already acquired the mutex means a reconnection process is already underway so thread can return immediately after previous thread release the mutex (line 15).
With this if a thread is already initializing the resource other threads can wait and detect it. One problem is if connection initialization failed other threads does not have a way to identify that. But that also can be easily dealt with another boolean variable to check the status of the connections at line 14.
This may not be the only way to do this - only the way came in to my mind. Please comment if you have any improvements over this. Thanks
One solution is to synchronous access to initConnection() method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Client { private AtomicReference<Connection> connection = new AtomicReference<Connection>(); private Semaphore mutex = new Semaphore(1); ... private void initConnection() throws InterruptedException { try { mutex.acquire(); Connection c = new Connection(); c.connect(); connection.set(c); } finally { mutex.release(); } } ... } |
Accessing connection object through AtomicReference make sure thread safety when multiple threads modify it.
Now concurrency issues have been dealt with. But practically there will be another problem in a condition of considerable number of threads accesses initConnection() method.
Consider two threads A and B. Thread A will acquire mutex and calling c.connect() method where thread B will wait at line 10. After thread A successfully initiate the connection and set to AtomicReference, thread B will acquire the mutex and do the same thing again. Causing unnecessarily reconnect. This might be noticeable specially as the concurrent access increases.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class Client { private final AtomicReference<Connection> connection = new AtomicReference<Connection>(); private final Semaphore mutex = new Semaphore(1); private final Semaphore mutexStatus = new Semaphore(1); ... private void initConnection() throws InterruptedException { boolean needToReconnect = false; try { needToReconnect = mutexStatus.tryAcquire(); mutex.acquire(); if (!needToReconnect) { return; } Connection c = new Connection(); c.connect(); connection.set(c); } finally { mutex.release(); if (needToReconnect) { mutexStatus.release(); } } } ... } |
As a solution another Semaphore is included to check the status of the mutex - to check whether someone have already inside the mutex. tryAcquire() method can be used in this case. If another thread already acquired the mutex means a reconnection process is already underway so thread can return immediately after previous thread release the mutex (line 15).
With this if a thread is already initializing the resource other threads can wait and detect it. One problem is if connection initialization failed other threads does not have a way to identify that. But that also can be easily dealt with another boolean variable to check the status of the connections at line 14.
1 2 3 | if (!needToReconnect && connection.get().isConnected()) { return; } |
This may not be the only way to do this - only the way came in to my mind. Please comment if you have any improvements over this. Thanks