Reputation: 4101
I have the following logic (simplified):
public class Application {
public static volatile boolean stopServer;
private static ScheduledExecutorService taskScheduler;
private static Thread listenerThread;
public static synchronized void switchStopServer() {
stopServer = true;
listenerThread.interrupt();
taskScheduler.shutdownNow();
}
public static void main(String[] args) {
int threadPoolSize = 4;
taskScheduler = Executors.newScheduledThreadPool(threadPoolSize);
listenerThread = new ListenerThread();
taskScheduler.schedule(listenerThread, 0, TimeUnit.NANOSECONDS);
}
}
public class ListenerThread extends Thread {
private static ServerSocket serverSocket;
private Socket socketConnection;
@Override
public void run() {
while (!Application.stopServer) {
try {
socketConnection = serverSocket.accept();
new CommunicatorThread(socketConnection).start();
} catch (SocketException e) {
} catch (Exception e) {
}
}
}
private static void closeServerSocket() {
try {
if (serverSocket != null && !serverSocket.isClosed()) serverSocket.close();
} catch (Exception e) { }
}
@Override
public void interrupt() {
closeServerSocket();
super.interrupt();
}
}
What I want to achieve, is to terminate Thread
s the proper way. First of all, is this (switchStopServer()
) the correct way to do that, or are there any better solutions?
I'm a little confused with the ScheduledExecutorService
, because shutdownNow()
does not interrupt the Thread
s, neither does ScheduledFuture.cancel(true)
(at least for me it doesn't), so I can't interrupt ServerSocket.accept()
. I know, in my example there is no need for the ScheduledExecutorService
, but in my real application there is.
Upvotes: 1
Views: 1891
Reputation: 116918
Your problem I believe is that you are confusing Thread
and Runnable
. Even though ListenerThread
extends Thread
, it is actually not it's own thread. The thread is managed by the ExecutorService
thread-pool which is just calling your run()
method. This is only [sort of] working because Thread
also implements Runnable
. When you call ListenerThread.interrupt()
you are not interrupting the thread in the thread-pool although you are calling your interrupt()
method but just directly in the calling thread. This should close the socket since it calls closeServerSocket()
from the outside.
When you call ScheduledFuture.cancel(true)
or shutdownNow()
, the pool thread(s) should be interrupted but this will not call your interrupt()
method there. You can test for the interruption by using Thread.currentThread().isInterrupted()
in your run()
method.
You should change ListenerThread
from extending Thread
and instead have it just implement Runnable
(see edit below). You will want to do something like the following loop in your run()
method:
while (!Application.stopServer && !Thread.currentThread().isInterrupted()) {
To interrupt the accept()
method, you are going to have to close the serverSocket
from another thread. Most likely this will be done by the thread that is calling interrupt()
. It should close the socket, shutdownNow()
or cancel()
the thread-pool, and then it can wait for the pool to terminate.
Edit:
Actually, I wonder why you are using a pool for your ListenerThread
since there will only ever be one of them, it is being scheduled immediately, and it is just starting a new thread on any connection directly. I would remove your taskScheduler
pool entirely, keep ListenerThread
extending Thread
, and just call new ListenerThread().start();
.
The outer thread would still just close the serverSocket
to stop the ListenerThread
. If you also need to close all of the connections as well then the ListenerThread
needs to keep a collection of the socketConnection
around so it can call close()
on them when the accept()
throws a n IOException
.
Also, currently you have private Socket socketConnection;
which is misleading because it will change after every call to accept()
. I'd rewrite it as:
Socket socketConnection = serverSocket.accept();
new CommunicatorThread(socketConnection).start();
Upvotes: 4