18

I would like to set name for threads of the ForkJoinPool used by work stealing pool, supplied by

ExecutorService newWorkStealingPool(int parallelism)

or

ExecutorService newWorkStealingPool()

So far I could not find a way to set custom names on threads used by this ExecutorService, is there a way?

newWorkStealingPool() basically supplies a ForkJoinPool, but ForkJoinPool also doesn't have a public constructor with supplied name pattern.

update: I have now found this constructor of ForkJoinPool which takes a thread factory ForkJoinPool.ForkJoinWorkerThreadFactory. But factory should return a ForkJoinWorkerThread, which doesn't have a public constructor. So I guess I will have to subclass ForkJoinWorkerThread.

cpz
  • 1,002
  • 2
  • 12
  • 27
  • Isn't that just a convenience method? You can make your own ForkJoinPool with a thread factory that does the naming for you. – pvg Dec 16 '15 at 02:53
  • Oops, don't know but somehow missed this. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html#ForkJoinPool-int-java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory-java.lang.Thread.UncaughtExceptionHandler-boolean- Yup, as you said that was my thought too but failed to notice mentioned constructor. – cpz Dec 16 '15 at 02:59

3 Answers3

45

This seems to be the minimum required code, reusing the existing default factory:

final ForkJoinWorkerThreadFactory factory = new ForkJoinWorkerThreadFactory() {
    @Override           
    public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
        worker.setName("my-thread-prefix-name-" + worker.getPoolIndex());
        return worker;
    }
};
    
forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), factory, null, false); 
Sergey
  • 3,253
  • 2
  • 33
  • 55
Morten Haraldsen
  • 1,013
  • 12
  • 24
  • 1
    Looks good except for the fact that `Executors.newWorkStealingPool()` sets the last argument (`async`) to `true`. You should probably do the same. – Gili Feb 06 '18 at 20:24
  • Nice catch. In general, the [documentation](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html#ForkJoinPool-int-java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory-java.lang.Thread.UncaughtExceptionHandler-boolean-) for the async parameter is: > if true, establishes local first-in-first-out scheduling mode for forked tasks > that are never joined. This mode may be more appropriate than default > locally stack-based mode in applications in which worker threads only > process event-style asynchronous tasks. For default value, use false. – Morten Haraldsen Feb 09 '18 at 07:46
  • 3
    I tried an example, and worker.getPoolIndex() always returns zero. – k.liakos Jan 06 '22 at 08:18
  • 1
    @k.liakos you are correct. I don't think you can invoke `worker.getPoolIndex()` before the pool has started. In any case, you could create a static `AtomicLong` field and generate IDs using `AtomicLong.getAndIncrement()`. – Gili Jun 22 '22 at 03:54
5

(Answering your update)

The following should allow you full control over the threads produced by your ForkJoinPools. In my case I wanted to be able to do "dangerous" stuff like access system properties. The default implementation uses java.util.concurrent.ForkJoinWorkerThread.InnocuousForkJoinWorkerThread which has a security manager and zero permissions.

public class MyForkJoinThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {
  @Override
  public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
    return new NotSoInnocuousWorkerThread(pool);
  }
}

and the worker thread (that now has all the same permissions as the rest of your application is this like this, plus whatever else you wanted....

public class NotSoInnocuousWorkerThread extends ForkJoinWorkerThread {
  protected NotSoInnocuousWorkerThread(ForkJoinPool pool) {
    super(pool);
  }
}

And you need to either pass the following property or set it in your code like this:

System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", 
                   MyForkJoinThreadFactory.class.getName());
Gus
  • 6,719
  • 6
  • 37
  • 58
  • I had already updated the question, method suggested is same as what I had done in the end. thanks – cpz Jun 09 '16 at 07:13
3

Here is @Morten Haraldsen's answer ported to Kotlin:

val forkJoinPool =
    ForkJoinPool.ForkJoinWorkerThreadFactory { pool ->
        ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool).apply {
            name = "my-thread-prefix-name-$poolIndex"
        }
    }.let { factory -> ForkJoinPool(Runtime.getRuntime().availableProcessors(), factory, null, true) }

Matt Klein
  • 7,856
  • 6
  • 45
  • 46
  • 1
    the OP mentioned Java, not Kotlin. – mr.M Oct 21 '20 at 09:56
  • 1
    Thanks for the down vote explanation. When searching for how to do this in Kotlin this question is near the top of the results so this answer still seems valuable to me. – Matt Klein Oct 21 '20 at 15:31
  • 1
    And the OP does not mention Java, they ask about ForkJoinPool which is a JVM class usable from Java, Kotlin, Scala, etc. – Matt Klein Oct 21 '20 at 15:36
  • 1
    there is the Java tag for this question – mr.M Oct 22 '20 at 07:28
  • 1
    There is also a `java.util.concurrent` tag, which is usable from all the JVM languages, like Kotlin. I don't see how having a Kotlin answer for something from the [Java Class Library](https://en.wikipedia.org/wiki/Java_Class_Library), which is available to all JVM languages, is negative in any way. – Matt Klein Oct 23 '20 at 21:49
  • let's write the examples for Scala/Groovy/Closure as well then – mr.M Oct 24 '20 at 15:02
  • 2
    @mr.M I think you meant Clojure. – Adwait Kumar Oct 29 '20 at 06:34