8056152: API to create Threads that do not inherit inheritable thread-local initial values
authorchegar
Fri, 18 Dec 2015 16:06:24 +0000
changeset 34716 7477a052aecc
parent 34715 efac989a60dd
child 34717 0c2b691e33c8
8056152: API to create Threads that do not inherit inheritable thread-local initial values Reviewed-by: alanb, dholmes, mchung, mr, rriggs
jdk/src/java.base/share/classes/java/lang/InheritableThreadLocal.java
jdk/src/java.base/share/classes/java/lang/Thread.java
jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java
jdk/src/java.base/share/classes/java/lang/ref/Reference.java
jdk/src/java.base/share/classes/sun/misc/GC.java
jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java
jdk/src/java.base/share/classes/sun/misc/Signal.java
jdk/src/java.base/share/classes/sun/net/NetworkServer.java
jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java
jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java
jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java
jdk/src/java.base/share/classes/sun/nio/fs/PollingWatchService.java
jdk/src/java.base/share/classes/sun/security/provider/SeedGenerator.java
jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java
jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java
jdk/test/java/lang/Thread/ITLConstructor.java
--- a/jdk/src/java.base/share/classes/java/lang/InheritableThreadLocal.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/InheritableThreadLocal.java	Fri Dec 18 16:06:24 2015 +0000
@@ -40,6 +40,11 @@
  * maintained in the variable (e.g., User ID, Transaction ID) must be
  * automatically transmitted to any child threads that are created.
  *
+ * <p>Note: During the creation of a new {@link
+ * Thread#Thread(ThreadGroup,Runnable,String,long,boolean) thread}, it is
+ * possible to <i>opt out</i> of receiving initial values for inheritable
+ * thread-local variables.
+ *
  * @author  Josh Bloch and Doug Lea
  * @see     ThreadLocal
  * @since   1.2
--- a/jdk/src/java.base/share/classes/java/lang/Thread.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/Thread.java	Fri Dec 18 16:06:24 2015 +0000
@@ -344,11 +344,11 @@
 
     /**
      * Initializes a Thread with the current AccessControlContext.
-     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext)
+     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
      */
     private void init(ThreadGroup g, Runnable target, String name,
                       long stackSize) {
-        init(g, target, name, stackSize, null);
+        init(g, target, name, stackSize, null, true);
     }
 
     /**
@@ -361,9 +361,12 @@
      *        zero to indicate that this parameter is to be ignored.
      * @param acc the AccessControlContext to inherit, or
      *            AccessController.getContext() if null
+     * @param inheritThreadLocals if {@code true}, inherit initial values for
+     *            inheritable thread-locals from the constructing thread
      */
     private void init(ThreadGroup g, Runnable target, String name,
-                      long stackSize, AccessControlContext acc) {
+                      long stackSize, AccessControlContext acc,
+                      boolean inheritThreadLocals) {
         if (name == null) {
             throw new NullPointerException("name cannot be null");
         }
@@ -414,7 +417,7 @@
                 acc != null ? acc : AccessController.getContext();
         this.target = target;
         setPriority(priority);
-        if (parent.inheritableThreadLocals != null)
+        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
             this.inheritableThreadLocals =
                 ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
         /* Stash the specified stack size in case the VM cares */
@@ -468,7 +471,7 @@
      * This is not a public constructor.
      */
     Thread(Runnable target, AccessControlContext acc) {
-        init(null, target, "Thread-" + nextThreadNum(), 0, acc);
+        init(null, target, "Thread-" + nextThreadNum(), 0, acc, true);
     }
 
     /**
@@ -678,6 +681,62 @@
     }
 
     /**
+     * Allocates a new {@code Thread} object so that it has {@code target}
+     * as its run object, has the specified {@code name} as its name,
+     * belongs to the thread group referred to by {@code group}, has
+     * the specified {@code stackSize}, and inherits initial values for
+     * {@linkplain InheritableThreadLocal inheritable thread-local} variables
+     * if {@code inheritThreadLocals} is {@code true}.
+     *
+     * <p> This constructor is identical to {@link
+     * #Thread(ThreadGroup,Runnable,String,long)} with the added ability to
+     * suppress, or not, the inheriting of initial values for inheritable
+     * thread-local variables from the constructing thread. This allows for
+     * finer grain control over inheritable thread-locals. Care must be taken
+     * when passing a value of {@code false} for {@code inheritThreadLocals},
+     * as it may lead to unexpected behavior if the new thread executes code
+     * that expects a specific thread-local value to be inherited.
+     *
+     * <p> Specifying a value of {@code true} for the {@code inheritThreadLocals}
+     * parameter will cause this constructor to behave exactly like the
+     * {@code Thread(ThreadGroup, Runnable, String, long)} constructor.
+     *
+     * @param  group
+     *         the thread group. If {@code null} and there is a security
+     *         manager, the group is determined by {@linkplain
+     *         SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.
+     *         If there is not a security manager or {@code
+     *         SecurityManager.getThreadGroup()} returns {@code null}, the group
+     *         is set to the current thread's thread group.
+     *
+     * @param  target
+     *         the object whose {@code run} method is invoked when this thread
+     *         is started. If {@code null}, this thread's run method is invoked.
+     *
+     * @param  name
+     *         the name of the new thread
+     *
+     * @param  stackSize
+     *         the desired stack size for the new thread, or zero to indicate
+     *         that this parameter is to be ignored
+     *
+     * @param  inheritThreadLocals
+     *         if {@code true}, inherit initial values for inheritable
+     *         thread-locals from the constructing thread, otherwise no initial
+     *         values are inherited
+     *
+     * @throws  SecurityException
+     *          if the current thread cannot create a thread in the specified
+     *          thread group
+     *
+     * @since 9
+     */
+    public Thread(ThreadGroup group, Runnable target, String name,
+                  long stackSize, boolean inheritThreadLocals) {
+        init(group, target, name, stackSize, null, inheritThreadLocals);
+    }
+
+    /**
      * Causes this thread to begin execution; the Java Virtual Machine
      * calls the <code>run</code> method of this thread.
      * <p>
--- a/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java	Fri Dec 18 16:06:24 2015 +0000
@@ -29,7 +29,6 @@
 import java.security.AccessController;
 import jdk.internal.misc.JavaLangAccess;
 import jdk.internal.misc.SharedSecrets;
-import sun.misc.ManagedLocalsThread;
 import sun.misc.VM;
 
 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
@@ -131,7 +130,7 @@
                     for (ThreadGroup tgn = tg;
                          tgn != null;
                          tg = tgn, tgn = tg.getParent());
-                    Thread sft = new ManagedLocalsThread(tg, proc, "Secondary finalizer");
+                    Thread sft = new Thread(tg, proc, "Secondary finalizer", 0, false);
                     sft.start();
                     try {
                         sft.join();
@@ -190,10 +189,10 @@
                 }}});
     }
 
-    private static class FinalizerThread extends ManagedLocalsThread {
+    private static class FinalizerThread extends Thread {
         private volatile boolean running;
         FinalizerThread(ThreadGroup g) {
-            super(g, "Finalizer");
+            super(g, null, "Finalizer", 0, false);
         }
         public void run() {
             // in case of recursive call to run()
--- a/jdk/src/java.base/share/classes/java/lang/ref/Reference.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/ref/Reference.java	Fri Dec 18 16:06:24 2015 +0000
@@ -29,7 +29,6 @@
 import jdk.internal.HotSpotIntrinsicCandidate;
 import jdk.internal.misc.JavaLangRefAccess;
 import jdk.internal.misc.SharedSecrets;
-import sun.misc.ManagedLocalsThread;
 
 /**
  * Abstract base class for reference objects.  This class defines the
@@ -128,7 +127,7 @@
 
     /* High-priority thread to enqueue pending References
      */
-    private static class ReferenceHandler extends ManagedLocalsThread {
+    private static class ReferenceHandler extends Thread {
 
         private static void ensureClassInitialized(Class<?> clazz) {
             try {
@@ -147,7 +146,7 @@
         }
 
         ReferenceHandler(ThreadGroup g, String name) {
-            super(g, name);
+            super(g, null, name, 0, false);
         }
 
         public void run() {
--- a/jdk/src/java.base/share/classes/sun/misc/GC.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/misc/GC.java	Fri Dec 18 16:06:24 2015 +0000
@@ -82,7 +82,7 @@
      */
     public static native long maxObjectInspectionAge();
 
-    private static class Daemon extends ManagedLocalsThread {
+    private static class Daemon extends Thread {
 
         public void run() {
             for (;;) {
@@ -122,7 +122,7 @@
         }
 
         private Daemon(ThreadGroup tg) {
-            super(tg, "GC Daemon");
+            super(tg, null, "GC Daemon", 0L, false);
         }
 
         /* Create a new daemon thread in the root thread group */
--- a/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java	Fri Dec 18 16:06:24 2015 +0000
@@ -35,8 +35,10 @@
  * A thread that has no permissions, is not a member of any user-defined
  * ThreadGroup and supports the ability to erase ThreadLocals.
  */
-public final class InnocuousThread extends ManagedLocalsThread {
+public final class InnocuousThread extends Thread {
     private static final jdk.internal.misc.Unsafe UNSAFE;
+    private static final long THREAD_LOCALS;
+    private static final long INHERITABLE_THREAD_LOCALS;
     private static final ThreadGroup INNOCUOUSTHREADGROUP;
     private static final AccessControlContext ACC;
     private static final long INHERITEDACCESSCONTROLCONTEXT;
@@ -54,7 +56,7 @@
     }
 
     public InnocuousThread(ThreadGroup group, Runnable target, String name) {
-        super(group, target, name);
+        super(group, target, name, 0L, false);
         UNSAFE.putOrderedObject(this, INHERITEDACCESSCONTROLCONTEXT, ACC);
         UNSAFE.putOrderedObject(this, CONTEXTCLASSLOADER, ClassLoader.getSystemClassLoader());
     }
@@ -73,6 +75,14 @@
             throw new SecurityException("setContextClassLoader");
     }
 
+    /**
+     * Drops all thread locals (and inherited thread locals).
+     */
+    public final void eraseThreadLocals() {
+        UNSAFE.putObject(this, THREAD_LOCALS, null);
+        UNSAFE.putObject(this, INHERITABLE_THREAD_LOCALS, null);
+    }
+
     // ensure run method is run only once
     private volatile boolean hasRun;
 
@@ -96,6 +106,10 @@
             Class<?> tk = Thread.class;
             Class<?> gk = ThreadGroup.class;
 
+            THREAD_LOCALS = UNSAFE.objectFieldOffset
+                    (tk.getDeclaredField("threadLocals"));
+            INHERITABLE_THREAD_LOCALS = UNSAFE.objectFieldOffset
+                    (tk.getDeclaredField("inheritableThreadLocals"));
             INHERITEDACCESSCONTROLCONTEXT = UNSAFE.objectFieldOffset
                 (tk.getDeclaredField("inheritedAccessControlContext"));
             CONTEXTCLASSLOADER = UNSAFE.objectFieldOffset
--- a/jdk/src/java.base/share/classes/sun/misc/Signal.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/misc/Signal.java	Fri Dec 18 16:06:24 2015 +0000
@@ -213,7 +213,7 @@
             }
         };
         if (handler != null) {
-            new ManagedLocalsThread(runnable, sig + " handler").start();
+            new Thread(null, runnable, sig + " handler", 0, false).start();
         }
     }
 
--- a/jdk/src/java.base/share/classes/sun/net/NetworkServer.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/net/NetworkServer.java	Fri Dec 18 16:06:24 2015 +0000
@@ -27,7 +27,6 @@
 import java.io.*;
 import java.net.Socket;
 import java.net.ServerSocket;
-import sun.misc.ManagedLocalsThread;
 
 /**
  * This is the base class for network servers.  To define a new type
@@ -73,7 +72,7 @@
                     NetworkServer n = (NetworkServer)clone();
                     n.serverSocket = null;
                     n.clientSocket = ns;
-                    new ManagedLocalsThread(n).start();
+                    new Thread(null, n, "NetworkServer", 0, false).start();
                 } catch(Exception e) {
                     System.out.print("Server failure\n");
                     e.printStackTrace();
@@ -108,7 +107,7 @@
         for each new connection. */
     public final void startServer(int port) throws IOException {
         serverSocket = new ServerSocket(port, 50);
-        serverInstance = new ManagedLocalsThread(this);
+        serverInstance = new Thread(null, this, "NetworkServer", 0, false);
         serverInstance.start();
     }
 
--- a/jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java	Fri Dec 18 16:06:24 2015 +0000
@@ -27,9 +27,8 @@
 import java.net.URL;
 import java.io.*;
 import java.util.StringTokenizer;
-import sun.misc.ManagedLocalsThread;
 
-class MimeLauncher extends ManagedLocalsThread {
+class MimeLauncher extends Thread {
     java.net.URLConnection uc;
     MimeEntry m;
     String genericTempFileTemplate;
@@ -38,7 +37,7 @@
 
     MimeLauncher (MimeEntry M, java.net.URLConnection uc,
                   InputStream is, String tempFileTemplate, String threadName) throws ApplicationLaunchException {
-        super(threadName);
+        super(null, null, threadName, 0, false);
         m = M;
         this.uc = uc;
         this.is = is;
--- a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java	Fri Dec 18 16:06:24 2015 +0000
@@ -30,7 +30,6 @@
 import java.security.PrivilegedAction;
 import java.io.IOException;
 import java.util.*;
-import sun.misc.ManagedLocalsThread;
 
 /**
  * Base implementation of background poller thread used in watch service
@@ -60,7 +59,7 @@
         AccessController.doPrivileged(new PrivilegedAction<>() {
             @Override
             public Object run() {
-                Thread thr = new ManagedLocalsThread(thisRunnable);
+                Thread thr = new Thread(null, thisRunnable, "FileSystemWatchService", 0, false);
                 thr.setDaemon(true);
                 thr.start();
                 return null;
--- a/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java	Fri Dec 18 16:06:24 2015 +0000
@@ -25,7 +25,6 @@
 
 package sun.nio.fs;
 
-import sun.misc.ManagedLocalsThread;
 import jdk.internal.misc.Unsafe;
 import java.util.concurrent.ExecutionException;
 
@@ -118,7 +117,7 @@
      * thread by writing into the memory location that it polls cooperatively.
      */
     static void runInterruptibly(Cancellable task) throws ExecutionException {
-        Thread t = new ManagedLocalsThread(task);
+        Thread t = new Thread(null, task, "NIO-Task", 0, false);
         t.start();
         boolean cancelledByInterrupt = false;
         while (t.isAlive()) {
--- a/jdk/src/java.base/share/classes/sun/nio/fs/PollingWatchService.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/nio/fs/PollingWatchService.java	Fri Dec 18 16:06:24 2015 +0000
@@ -35,7 +35,6 @@
 import java.util.*;
 import java.util.concurrent.*;
 import com.sun.nio.file.SensitivityWatchEventModifier;
-import sun.misc.ManagedLocalsThread;
 
 /**
  * Simple WatchService implementation that uses periodic tasks to poll
@@ -59,7 +58,7 @@
             .newSingleThreadScheduledExecutor(new ThreadFactory() {
                  @Override
                  public Thread newThread(Runnable r) {
-                     Thread t = new ManagedLocalsThread(r);
+                     Thread t = new Thread(null, r, "FileSystemWatchService", 0, false);
                      t.setDaemon(true);
                      return t;
                  }});
--- a/jdk/src/java.base/share/classes/sun/security/provider/SeedGenerator.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/security/provider/SeedGenerator.java	Fri Dec 18 16:06:24 2015 +0000
@@ -75,7 +75,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Random;
-import sun.misc.ManagedLocalsThread;
 import sun.security.util.Debug;
 
 abstract class SeedGenerator {
@@ -305,9 +304,11 @@
                             }
                             finalsg[0] = new ThreadGroup
                                 (group, "SeedGenerator ThreadGroup");
-                            Thread newT = new ManagedLocalsThread(finalsg[0],
+                            Thread newT = new Thread(finalsg[0],
                                 ThreadedSeedGenerator.this,
-                                "SeedGenerator Thread");
+                                "SeedGenerator Thread",
+                                0,
+                                false);
                             newT.setPriority(Thread.MIN_PRIORITY);
                             newT.setDaemon(true);
                             return newT;
@@ -342,8 +343,8 @@
                         // Start some noisy threads
                         try {
                             BogusThread bt = new BogusThread();
-                            Thread t = new ManagedLocalsThread
-                                (seedGroup, bt, "SeedGenerator Thread");
+                            Thread t = new Thread
+                                (seedGroup, bt, "SeedGenerator Thread", 0, false);
                             t.start();
                         } catch (Exception e) {
                             throw new InternalError("internal error: " +
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java	Fri Dec 18 16:06:24 2015 +0000
@@ -40,7 +40,6 @@
 
 import javax.crypto.BadPaddingException;
 import javax.net.ssl.*;
-import sun.misc.ManagedLocalsThread;
 
 import jdk.internal.misc.JavaNetInetAddressAccess;
 import jdk.internal.misc.SharedSecrets;
@@ -1153,10 +1152,13 @@
                         HandshakeCompletedEvent event =
                             new HandshakeCompletedEvent(this, sess);
 
-                        Thread thread = new ManagedLocalsThread(
+                        Thread thread = new Thread(
+                            null,
                             new NotifyHandshake(
                                 handshakeListeners.entrySet(), event),
-                            "HandshakeCompletedNotify-Thread");
+                            "HandshakeCompletedNotify-Thread",
+                            0,
+                            false);
                         thread.start();
                     }
                 }
--- a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java	Fri Dec 18 14:43:24 2015 +0100
+++ b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java	Fri Dec 18 16:06:24 2015 +0000
@@ -40,7 +40,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
-import sun.misc.ManagedLocalsThread;
 
 /**
  * A multi-threaded implementation of Selector for Windows.
@@ -404,13 +403,14 @@
     }
 
     // Represents a helper thread used for select.
-    private final class SelectThread extends ManagedLocalsThread {
+    private final class SelectThread extends Thread {
         private final int index; // index of this thread
         final SubSelector subSelector;
         private long lastRun = 0; // last run number
         private volatile boolean zombie;
         // Creates a new thread
         private SelectThread(int i) {
+            super(null, null, "SelectorHelper", 0, false);
             this.index = i;
             this.subSelector = new SubSelector(i);
             //make sure we wait for next round of poll
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/Thread/ITLConstructor.java	Fri Dec 18 16:06:24 2015 +0000
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Basic test for Thread(ThreadGroup,Runnable,String,long,boolean)
+ */
+
+public class ITLConstructor {
+    static InheritableThreadLocal<Integer> n = new InheritableThreadLocal<>() {
+        protected Integer initialValue() {
+            return 0;
+        }
+
+        protected Integer childValue(Integer parentValue) {
+            return parentValue + 1;
+        }
+    };
+
+    static final int CHILD_THREAD_COUNT = 10;
+
+    public static void main(String args[]) throws Exception {
+        test(true);
+        test(false);
+    }
+
+    static void test(boolean inherit) throws Exception {
+        // concurrent access to separate indexes is ok
+        int[] x = new int[CHILD_THREAD_COUNT];
+        Thread child = new Thread(Thread.currentThread().getThreadGroup(),
+                                  new AnotherRunnable(0, x, inherit),
+                                  "ITLConstructor-thread-"+(0),
+                                   0,
+                                   inherit);
+        child.start();
+        child.join(); // waits for *all* threads to complete
+
+        // Check results
+        for(int i=0; i<CHILD_THREAD_COUNT; i++) {
+            int expectedValue = 1;
+            if (inherit)
+                expectedValue = i+1;
+
+            if (x[i] != expectedValue)
+                throw (new Exception("Got x[" + i + "] = " + x[i]
+                                     + ", expected: " + expectedValue));
+        }
+    }
+
+    static class AnotherRunnable implements Runnable {
+        final int threadId;
+        final int[] x;
+        final boolean inherit;
+        AnotherRunnable(int threadId, int[] x, boolean inherit) {
+            this.threadId = threadId;
+            this.x = x;
+            this.inherit = inherit;
+        }
+
+        public void run() {
+            int itlValue = n.get();
+
+            if (threadId < CHILD_THREAD_COUNT-1) {
+                Thread child = new Thread(Thread.currentThread().getThreadGroup(),
+                                          new AnotherRunnable(threadId+1, x, inherit),
+                                          "ITLConstructor-thread-" + (threadId+1),
+                                          0,
+                                          inherit);
+                child.start();
+                try {
+                    child.join();
+                } catch(InterruptedException e) {
+                    throw(new RuntimeException("Interrupted", e));
+                }
+            }
+
+            x[threadId] = itlValue+1;
+        }
+    }
+}