Nate Lockwood wrote:Question: does this thread then die or does it just languish in some sort of cyber purgatory? Will I need to call garbage collection at this point?
That depends a little. As long as you can reliably say the run() method comes to an end (this is your code so only you can answer this), then what happens to the Thread that runs the Runnable is dependent on your ThreadPoolExecutor. Depending on the configuration, the Thread may hang around in a pool to be used later on by another request to run via the same ThreadPoolExecutor. Or it could get destroyed by the Executor right away, or after some period of time. What you have to worry about is that the run() method comes to an end, and that no resources are left stored in the Thread (for example any ThreadLocal references are cleared).
The other thread, talkWorker is blocked waiting to take data from a blocking queue and then write to the socket. I have not seen an exception thrown here (although I think it might be if it tried to write data to a disconnected socket). Since no exception is caught I can't use the sentinel
Questiion: how do I signal it to stop? Would I send an interrupt to it from listenWorker? I can catch that exception I think.
There are a couple of ways to do this. One way is to interrupt the talkWorker's thread, catch the exception and check the sentinel status. If the status is set to false, then stop listening and break out of the loop. Otherwise report the exception and try again. This may require some work (a method on talkWorker like 'stop()' which sets isConnected to false then uses a reference to the thread running the run() method to interrupt it, and passing a reference to the talkWorker to whomever knows when the process should stop).
Since you already have a communication channel between the talkWorker and the listenWorker (the blocking queue I think) then you could use a different approach - one that is called a 'Poison Pill'. You create a specific Object and pass it both to the producer (whomever sends data to the blocking queue) and the consumer (the talkWorker). When the producer wants the consumer to stop working you send the Poison Pill onto the Queue. On the consumer side the consumer reads from the queue and the first thing it does is compare what it gets to the Poison Pill (using == since you want to make sure the identity of the Object is exact). If they are equal the talkWorker doesn't try to send anything to down the socket, it instead breaks out of the loop and shuts down.
I expect that I will also need to close the socket so I don't bleed resources.
Yes, do this in a finally block so it gets shut down no matter what, but also surround it in a try/catch since closing can fail if something else caused it to close already (which if you are not careful could hide other exceptions).
Do I make it null, too?
That depends on scope.
You should be keeping the reference in the smallest scope possible, so when it closes either it goes out of scope (and so doesn't need to be set to null). Don't create references to it that aren't necessary, and let your Objects which do reference it come to a natural death (don't hold references to them, don't try to make the re-runnable, make sure their methods all come to an end safely).
Do I need to do anything with the threadpool executors or will they detect this automatically.
Just in the case of an exception in one thread? That depends on your
philosophy and how well you think you can handle keeping your Threads clean. If you can keep the ThreadLocal namespace clean and you can ensure your run() methods come to a clean end, then there is probably no need to do anything on the Executor - the Thread is free to be re-used if that is how the Executor is configured. On the other hand, there is a school of thought saying you shouldn't be so sure you can keep all the problems associated with an exception localized. If the exception occurs it could have side effects in multiple threads and could cause a cascade of problems. So you would be safe to shut it all down, kill the entire Executor and all threads in it, then restart all the Threads which were running (ensuring a fresh clean state). I think this is probably overkill in your case as long as you don't have a lot of shared state between threads and can keep things clean and isolated as described above.