FULL PRODUCT VERSION :
java version " 1.7.0_13 "
Java(TM) SE Runtime Environment (build 1.7.0_13-b20)
Java HotSpot(TM) Client VM (build 23.7-b01, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
OS version is irrelevant, but here it is: Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
If you want to destroy a ThreadGroup in a concurrency safe manner you have to do this:
final ThreadGroup tg = ...;
synchronized (tg) {
if ( !tg.isDestroyed() && tg.activeCount() == 0 ) {
tg.destroy();
}
}
However, this knowledge can only be obtained from looking at the implementation's source code. The JavaDocs do not mention it. See
http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadGroup.html
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Look at the JavaDoc at http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadGroup.html
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The documentation mentions in the header section (and possibly in method documentations as well) that a user of a ThreadGroup must hold the monitor of the instance to safely check whether the instance can be destroyed.
ACTUAL -
No hints in the documentation to the fact that thread safe destruction can be achieved by holding the monitor of the instance.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
If the race condition shows up it looks like this:
Exception in thread " main " java.lang.IllegalThreadStateException
at java.lang.ThreadGroup.destroy(ThreadGroup.java:775)
at ...
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package threads;
public final class SafeThreadGroupDestroy {
private static final class DummyRunner implements Runnable {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// NOP
}
}
}
public static void main(String[] args) throws InterruptedException {
testSuccess();
testFail();
}
private static void startCreator(final ThreadGroup tg,
final DummyRunner runner) {
new Thread(new Runnable() {
@Override
public void run() {
try {
for (;;) {
new Thread(tg, runner).start();
}
} catch (Throwable e) {
System.out.println( " stop creating " );
}
}
}).start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// NOP
}
}
private static void testSuccess() {
final ThreadGroup tg = new ThreadGroup( " test " );
final DummyRunner runner = new DummyRunner();
new Thread(tg, runner).start(); // ensure there is one thread
System.out.println( " active: " + tg.activeCount());
boolean destroyed = false;
do {
synchronized (tg) {
if (!tg.isDestroyed() && tg.activeCount() == 0) {
// simulate race condition: concurrent creation of another
// thread at this point in time.
startCreator(tg, runner);
tg.destroy();
destroyed = true;
}
}
} while (!destroyed);
System.out.println( " destroyed " );
}
private static void testFail() {
final ThreadGroup tg = new ThreadGroup( " test " );
final DummyRunner runner = new DummyRunner();
new Thread(tg, runner).start(); // ensure there is one thread
System.out.println( " active: " + tg.activeCount());
boolean destroyed = false;
do {
if (!tg.isDestroyed() && tg.activeCount() == 0) {
// simulate race condition: concurrent creation of another
// thread at this point in time.
startCreator(tg, runner);
tg.destroy();
destroyed = true;
}
} while (!destroyed);
System.out.println( " destroyed " );
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Inspect sources of ThreadGroup to find out that holding the mutex of this allows for thread safe destruction.
java version " 1.7.0_13 "
Java(TM) SE Runtime Environment (build 1.7.0_13-b20)
Java HotSpot(TM) Client VM (build 23.7-b01, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
OS version is irrelevant, but here it is: Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
If you want to destroy a ThreadGroup in a concurrency safe manner you have to do this:
final ThreadGroup tg = ...;
synchronized (tg) {
if ( !tg.isDestroyed() && tg.activeCount() == 0 ) {
tg.destroy();
}
}
However, this knowledge can only be obtained from looking at the implementation's source code. The JavaDocs do not mention it. See
http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadGroup.html
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Look at the JavaDoc at http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadGroup.html
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The documentation mentions in the header section (and possibly in method documentations as well) that a user of a ThreadGroup must hold the monitor of the instance to safely check whether the instance can be destroyed.
ACTUAL -
No hints in the documentation to the fact that thread safe destruction can be achieved by holding the monitor of the instance.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
If the race condition shows up it looks like this:
Exception in thread " main " java.lang.IllegalThreadStateException
at java.lang.ThreadGroup.destroy(ThreadGroup.java:775)
at ...
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package threads;
public final class SafeThreadGroupDestroy {
private static final class DummyRunner implements Runnable {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// NOP
}
}
}
public static void main(String[] args) throws InterruptedException {
testSuccess();
testFail();
}
private static void startCreator(final ThreadGroup tg,
final DummyRunner runner) {
new Thread(new Runnable() {
@Override
public void run() {
try {
for (;;) {
new Thread(tg, runner).start();
}
} catch (Throwable e) {
System.out.println( " stop creating " );
}
}
}).start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// NOP
}
}
private static void testSuccess() {
final ThreadGroup tg = new ThreadGroup( " test " );
final DummyRunner runner = new DummyRunner();
new Thread(tg, runner).start(); // ensure there is one thread
System.out.println( " active: " + tg.activeCount());
boolean destroyed = false;
do {
synchronized (tg) {
if (!tg.isDestroyed() && tg.activeCount() == 0) {
// simulate race condition: concurrent creation of another
// thread at this point in time.
startCreator(tg, runner);
tg.destroy();
destroyed = true;
}
}
} while (!destroyed);
System.out.println( " destroyed " );
}
private static void testFail() {
final ThreadGroup tg = new ThreadGroup( " test " );
final DummyRunner runner = new DummyRunner();
new Thread(tg, runner).start(); // ensure there is one thread
System.out.println( " active: " + tg.activeCount());
boolean destroyed = false;
do {
if (!tg.isDestroyed() && tg.activeCount() == 0) {
// simulate race condition: concurrent creation of another
// thread at this point in time.
startCreator(tg, runner);
tg.destroy();
destroyed = true;
}
} while (!destroyed);
System.out.println( " destroyed " );
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Inspect sources of ThreadGroup to find out that holding the mutex of this allows for thread safe destruction.