Java Specialists' Java Training Europehome of the java specialists' newsletter

The Java Specialists' Newsletter
Issue 2262015-02-24 Category: Tips and Tricks Java version: Java 8

GitHub Subscribe Free RSS Feed

Discovering Where Threads Are Being Constructed

by Dr. Heinz M. Kabutz
Abstract:
How can you discover all the places in your program where threads are being constructed? In this newsletter we create our own little SecurityManager to keep an eye on thread creation.

Welcome to the 226th issue of The Java(tm) Specialists' Newsletter, sent to you from the beautiful island of Crete. The mountains of Crete are covered with snow at this time of year. From where I'm sitting right now in our conference room, I have a beautiful view of the Lefka Ori mountain range in the distance. My neighbour's sleepy vineyard is to my left and I can see Mr Kostas walking around and inspecting his property. He was just a wee lad when Zorba the Greek was filmed in our village, and he got the role of running around and throwing stones. Perfect for a young Cretan boy. I'm sure my son would've enjoyed that too. In front of me is the sea. Most days the sun is too bright on the azure water and I need to close the shutters. I installed a projector that can output 4200 lumen, which allows my students to easily see the slides off the high-reflective projector screen whilst the shutters can be left open to enjoy the beautiful views. Here is what the sea view looks like from within the conference room:

NEW: Please see our new "Extreme Java" course, combining concurrency, a little bit of performance and Java 8. Extreme Java - Concurrency & Performance for Java 8.

Discovering Where Threads Are Being Constructed

This newsletter was prompted by a question by one of my readers, Scott Morgan from Adligo. He wanted to know whether there was a way in which we could somehow prevent threads from being created. Java does not have the concept of thread ownership. Once you have started a thread, you have no further claim over at. It immediately has full rights, same as its creator. We do have thread groups. These were used initially with Java, but since Java 5, there has not been much need for them. We use to handle uncaught exceptions with thread groups in the early days of Java. But nowadays, we have UncaughtExceptionHandler. Furthermore, in modern code, threads are partitioned using thread pools rather than thread groups.

So how can we control the creation and starting of threads? If you look inside the source code for the constructor of thread, you will notice a call to the security manager. This checks whether we are allowed to modify our current thread group. A thread automatically is created in the same thread group as its parent. Thus if we can check the permission, we could also determine whether the thread may be created or not.

The code I will present in this newsletter, would typically be used in a testing environment. It would not work if you already had a security manager installed. We present a ThreadWatcher security manager that will test a given predicate to determine whether an action should be executed. Let's start with the ThreadWatcher:

import java.security.*;
import java.util.function.*;

public class ThreadWatcher extends SecurityManager {
  private final Predicate<Thread> predicate;
  private final Consumer<Thread> action;

  public ThreadWatcher(Predicate<Thread> predicate,
                       Consumer<Thread> action) {
    this.predicate = predicate;
    this.action = action;
  }

  public void checkPermission(Permission perm) {
    // allow everything
  }

  public void checkPermission(Permission perm, Object context) {
    // allow everything
  }

  public void checkAccess(ThreadGroup g) {
    Thread creatingThread = Thread.currentThread();
    if (predicate.test(creatingThread)) {
      action.accept(creatingThread);
    }
  }
}
  

Let's say that we wanted to prevent threads within thread pools from creating new threads. This is an arbitrary rule, and just meant as an illustration. Typically, thread pool threads follow a particular naming convention, which we can find in the thread factory. It is of the form pool-#-thread-#, where the #'s would hopefully render the names unique. We could thus define a predicate that used a regular expression match on the thread name, such as a Lambda Predicate (Thread t) -> t.getName().matches("pool-\\d+-thread-\\d+")

We also define a simple job that prints "hello" plus the thread name and a consumer that throws a SecurityException if we try to create a thread for a context where the predicate matches. These are all provided by the DemoSupport interface:

import java.util.function.*;

public interface DemoSupport {
  static Predicate<Thread> createPredicate() {
    return (Thread t) ->
        t.getName().matches("pool-\\d+-thread-\\d+");
  }

  static Consumer<Thread> createConsumer() {
    return (Thread creator) -> {
      throw new SecurityException(creator +
          " tried to create a thread");
    };
  }

  static Runnable createHelloJob() {
    return () -> System.out.printf(
        "Hello from \"%s\"",
        Thread.currentThread()
    );
  }
}
  

In our first example, we try to create a new thread from within our main thread. This should work:

import java.util.concurrent.*;

public class ThreadFromMainThread {
  public static void main(String... args)
      throws InterruptedException {
    System.setSecurityManager(
        new ThreadWatcher(
            DemoSupport.createPredicate(),
            DemoSupport.createConsumer()
        )
    );

    new Thread(DemoSupport.createHelloJob(),
        "This should work 1").start();

    System.setSecurityManager(null);
  }
}
  

Output is this:

Hello from "Thread[This should work 1,5,main]"
  

On the other hand, if we try to create a new thread from within a thread pool thread, then the name will match our regular expression and it will fail:

import java.util.concurrent.*;

public class ThreadFromThreadPool {
  public static void main(String... args)
      throws InterruptedException {
    System.setSecurityManager(
        new ThreadWatcher(
            DemoSupport.createPredicate(),
            DemoSupport.createConsumer()
        )
    );

    ExecutorService pool = Executors.newFixedThreadPool(10);
    Future<?> future = pool.submit(() ->
            new Thread(DemoSupport.createHelloJob(),
                "This should print a warning 1")
    );
    try {
      future.get();
    } catch (ExecutionException e) {
      e.getCause().printStackTrace();
    }
    pool.shutdown();

    System.setSecurityManager(null);
  }
}
  

The output is now:

java.lang.SecurityException: Thread[pool-1-thread-1,5,main] \
    tried to create a thread
  at DemoSupport.lambda$createConsumer$1(DemoSupport.java:11)
  at DemoSupport$$Lambda$2/558638686.accept(Unknown Source)
  at ThreadWatcher.checkAccess(ThreadWatcher.java:25)
  at java.lang.ThreadGroup.checkAccess(ThreadGroup.java:315)
  at java.lang.Thread.init(Thread.java:391)
  at java.lang.Thread.init(Thread.java:349)
  at java.lang.Thread.<init>(Thread.java:548)
  at ThreadFromThreadPool.lambda$main$0(ThreadFromThreadPool:15)
  at ThreadFromThreadPool$$Lambda$3/1452126962.call
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker
  at java.util.concurrent.ThreadPoolExecutor$Worker.run
  at java.lang.Thread.run(Thread.java:745)
  

We could also get a bit more fancy with how we manage the threads who are misbehaving. For example, we could put all the miscreants into a map, with functions forEach() and toString() implemented to view the elements, such as:

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.stream.*;

public class ThreadAccumulator implements Consumer<Thread> {
  private final ConcurrentMap<Thread, LongAdder> miscreants =
      new ConcurrentHashMap<>();

  public void accept(Thread thread) {
    miscreants.computeIfAbsent(
        thread, t -> new LongAdder()).increment();
  }

  public int getNumberOfMisbehavingThreads() {
    return miscreants.size();
  }

  public void forEach(BiConsumer<Thread, Integer> action) {
    miscreants.entrySet()
        .forEach(e -> action.accept(
            e.getKey(),
            e.getValue().intValue()));
  }

  public String toString() {
    return miscreants.entrySet()
        .parallelStream()
        .map(ThreadAccumulator::format)
        .collect(Collectors.joining(", "));
  }

  private static String format(Map.Entry<Thread, LongAdder> e) {
    return String.format("%s created %d thread(s)",
        e.getKey().getName(),
        e.getValue().intValue());
  }
}
  

I must say, the new features in Java 8 are very nice. Have a look at how easy it is to convert the map of miscreants into a nice comma separated String in the toString() method! I also like the computeIfAbsent() method inside Map, which means that there is no more need for the putIfAbsent check.

Thanks for your feedback as always. If you'd like to say "hello", simply reply to this email or send me a note to heinz@javaspecialists.eu. I enjoy hearing from you :-)

Kind regards

Heinz

Tips and Tricks Articles Related Java Course

Extreme Java - Concurrency and Performance for Java 8
Extreme Java - Advanced Topics for Java 8
Design Patterns
In-House Courses

© 2010-2016 Heinz Kabutz - All Rights Reserved Sitemap
Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. JavaSpecialists.eu is not connected to Oracle, Inc. and is not sponsored by Oracle, Inc.