Reputation: 743
I have a card game app where the "Droid" hand has to choose among multiple discards to optimize its hand. So I am threading the possible discard choices and then calculating the hand valuation. I use two different approaches to hand valuation; one is exhaustive but can be very expensive, and the other is heuristic and very fast. I switch from the exhaustive approach to the heuristic approach once the computation time exceeds 500ms. I do this by starting the threads over the possible discards, and then also starting a sleep thread. When it finishes sleeping, it interrupts the other threads and I then rerun them with the heuristic approach.
All of this is working fine.But in checking for an interrupted thread in the join loop, I immediately throw an InterruptedException
so that the caller knows to rerun the calculation. In doing so, I don't ever join the other interrupted threads. Is that a resource leak, or will the other threads be cleaned up eventually?
Code snippets follow: Starting my threads:
for (Card disCard : cardsWithAdded) {
CardList cards = new CardList(cardsWithAdded);
cards.remove(disCard);
testHand[iThread] = new ThreadedHand(this.hand.getRoundOf(), cards, disCard, this.method, isFinalTurn); //creates new hand with replaced cards
t[iThread] = new Thread(threadGroup,testHand[iThread]);
t[iThread].start(); //calls meldAndEvaluate
iThread++;
}
Interrupting them in the sleep thread:
public void run() {
final long sleepStartTime = System.currentTimeMillis();
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
// if interrupted, this means the other threads finished their work before the sleep ended
return;
}
threadGroup.interrupt(); //interrupt the other calculation threads
}
Check for interruption in the calculation thread (in a loop):
if (Thread.interrupted()) {
throw new InterruptedException();
}
Setting a flag in run:
public void run() {
threadStart = System.currentTimeMillis();
wasThreadInterrupted = false;
try {
this.meldAndEvaluate(this.method, EasyComputerPlayer.this , this.isFinalTurn);
} catch (InterruptedException e) {
//just break out of this thread
threadStop = System.currentTimeMillis();
wasThreadInterrupted = true;
}
threadStop = System.currentTimeMillis();
}
And the join where I break at the first interrupted thread:
for (int iThread=0; iThread < this.numThreads; iThread++) {
try {
t[iThread].join();
} catch (InterruptedException e) {
throw new InterruptedException(String.format("%s-%s: findBestHandFinish interrupted in join()...",FiveKings.APP_TAG,Thread.currentThread().getName()));
}
//can only test this flag after we've rejoined the thread to this one
if (testHand[iThread].wasThreadInterrupted()) throw new InterruptedException(String.format("After join: Thread %s shows was-interrupted - throwing exception", t[iThread].getName()));
Upvotes: 1
Views: 83
Reputation: 27190
The call t.join()
doesn't do anything except wait for thread t
to die. It doesn't do anything at all to thread t
.
Upvotes: 0
Reputation: 719281
Joining the threads is neither necessary ... or sufficient to prevent resource leaks.
In reality:
The thread stack is eligible for reclamation when the thread has terminated1. If the thread does not respond appropriately to the interrupt (i.e. by terminating itself) then you have a resource leak.
The Thread
object is eligible for reclamation after the thread has terminated AND there are no reachable references to the object. But leakage of the Thread
object is a simple memory leak, not a resource leak in the normal sense.
1 - A thread terminates when the run() method terminates, either normally (by returning) or abnormally (as a result of the propagation of an unchecked exception).
Upvotes: 4