I'm currently writing a server which multiple clients will connect to. The communication protocol is basically that the server sends a task to the clients and the clients respond when the task is performed. The clients stay connected to the server and should never disconnect.
Currently I'm starting a new Thread to handle each client. In spirit my current solution is like this: (source from Java Concurrency in practice)
public class ThreadPerTaskWebServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
final Socket connection = socket.accept();
Runnable task = new Runnable() {
public void run() {
handleRequest(connection);
}
};
new Thread(task).start();
}
}
private static void handleRequest(Socket connection) {
// request-handling logic here
}
}
But I would like to use Executors since they seem more stable and scale better. So in order for the server to be able to send messages to the clients I would need to keep track of the clients separately from the Executor running them. So if I where to place the clients in an CopyOnWriteArrayList clients and then have something like this in the Server class:
Socket clientSocket = serverSocket.accept();
// The runnable that takes care of a client
ClientHandler c = new ClientHandler(clientSocket, this);
// Start executing the ClientHandler
exec.execute(c);
// Add clients to CopyOnWriteArrayList
clients.add(c);
This would allow me to iterate over the CopyOnWriteArrayList in order to send commands to all the clients and also remove disconnected ones. Would this the an robust solution? What would happen with the Executor if a runnable for some reason would propagate an exception to it?
Would this the an robust solution?
Yes, I think so. You need to make sure you either allocate the right number of threads in your fixed size pool or use a dynamic pool.
What would happen with the Executor if a runnable for some reason would propagate an exception to it?
If your code throws a RuntimeException
then the executor pool thread executing the code will stop but the pool thread count will be decremented and another thread created if the service has not already been shutdown. So the thread pool will continue to operate appropriately.
This would allow me to iterate over the CopyOnWriteArrayList in order to send commands to all the clients and also remove disconnected ones
You would not need to remove the disconnected ones unless you were saving a reference to them of course. Otherwise I can't really comment on this requirement.
If you choose the thread per connection approch and you don't want use NIO api, I believe this is a good solution. But remember that if your pool size is smaller than the number of concurrent requests you have clients pending until previous requests end.
About what happens if you got an exception in your task: the thread terminates but executor will create other threads.