@Test(timeout = 2000)
public void testDescendantThreadGroups() throws InterruptedException {

    final SecurityManager current = System.getSecurityManager();
    try {
        // install security manager that only allows parent thread groups to mess with descendant thread groups
        System.setSecurityManager(new SecurityManager() {
            @Override
            public void checkAccess(ThreadGroup g) {
                final ThreadGroup source = Thread.currentThread().getThreadGroup();
                if (source != null) {
                    if (!source.parentOf(g)) {
                        throw new SecurityException("source group is not an ancestor of the target group");
                    }
                    super.checkAccess(g);
                }
            }
            // so we can restore the security manager at the end of the test
            @Override
            public void checkPermission(Permission perm) {
            }
        });
        // holder for the thread factory, plays the role of a global singleton
        final AtomicReference<DefaultThreadFactory> factory = new AtomicReference<DefaultThreadFactory>();
        final AtomicInteger counter = new AtomicInteger();
        final Runnable task = new Runnable() {
            @Override
            public void run() {
                counter.incrementAndGet();
            }
        };
        final AtomicReference<Throwable> interrupted = new AtomicReference<Throwable>();
        // create the thread factory, since we are running the thread group brother, the thread
        // factory will now forever be tied to that group
        // we then create a thread from the factory to run a "task" for us
        final Thread first = new Thread(new ThreadGroup("brother"), new Runnable() {
            @Override
            public void run() {
                factory.set(new DefaultThreadFactory("test", false, Thread.NORM_PRIORITY, null));
                final Thread t = factory.get().newThread(task);
                t.start();
                try {
                    t.join();
                } catch (InterruptedException e) {
                    interrupted.set(e);
                    Thread.currentThread().interrupt();
                }
            }
        });
        first.start();
        first.join();
        assertNull(interrupted.get());
        // now we will use factory again, this time from a sibling thread group sister
        // if DefaultThreadFactory is "sticky" about thread groups, a security manager
        // that forbids sibling thread groups from messing with each other will strike this down
        final Thread second = new Thread(new ThreadGroup("sister"), new Runnable() {
            @Override
            public void run() {
                final Thread t = factory.get().newThread(task);
                t.start();
                try {
                    t.join();
                } catch (InterruptedException e) {
                    interrupted.set(e);
                    Thread.currentThread().interrupt();
                }
            }
        });
        second.start();
        second.join();
        assertNull(interrupted.get());
        assertEquals(2, counter.get());
    } finally {
        System.setSecurityManager(current);
    }
}