1
private static ThreadFactory doBuild(ThreadFactoryBuilder builder) {
    final String nameFormat = builder.nameFormat;
    final Boolean daemon = builder.daemon;
    final Integer priority = builder.priority;
    final UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler;
    final ThreadFactory backingThreadFactory =
        (builder.backingThreadFactory != null)
            ? builder.backingThreadFactory
            : Executors.defaultThreadFactory();
    final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null;
    return new ThreadFactory() {
      @Override
      public Thread newThread(Runnable runnable) {
        Thread thread = backingThreadFactory.newThread(runnable);
        if (nameFormat != null) {
          thread.setName(format(nameFormat, count.getAndIncrement()));
        }
        if (daemon != null) {
          thread.setDaemon(daemon);
        }
        if (priority != null) {
          thread.setPriority(priority);
        }
        if (uncaughtExceptionHandler != null) {
          thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
        }
        return thread;
      }
    };
  }

Recently I have began looking into ThreadFactory which is used by ThreadPoolExecutor to create new threads in the thread pool. For the convenience of debugging and monitoring we don't want the threads created by the thread pool to be the default 0,1,2,3 but rather with a meaningful name.

One way to achieve this goal is to implement a customized ThreadLoad that can set the name of thread when thread is created. Guava has got a handy builder class for customized ThreadFactory and I wish to learn from it.

It is not hard to understand most part of this class but I am quite confused by the count variable in the doBuild method.

I also went to the source code of ThreadPoolExecutor#Worker where the newThread() of the ThreadFactory is actually called.

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

But I am still not clear why we need an atomic variable here.

Of course I can guess the threads in the thread pool may be created in a multi-threading way thus to ensure the id of the threads not get duplicated we need the id-generator to be a atomic variable but I have got no direct evidence for this assumption yet.

Can anyone cast some light on that?

x00
  • 13,643
  • 3
  • 16
  • 40
Boyu Zhang
  • 219
  • 2
  • 12
  • 1
    Your guess is correct: "I can guess the threads in the thread pool may be created in a multi-threading way thus to ensure the id of the threads not get duplicated we need the id-generator to be a atomic variable" – dnault Jul 09 '20 at 04:59
  • @dnault Thanks,but this is just guessing the cause based on the result which is not the way people get valid conclusion? Where can I know direct evidence on it? – Boyu Zhang Jul 09 '20 at 05:03

1 Answers1

1

I doubt that you'll find any

direct evidence

in the code. There are only 3 possibilities:

  1. A comment in the code from the author explaining that AtomicLong is used for thread safety reasons. But it's still indirect evidence, because author can be wrong in his assumptions (he is't).
  2. A test that checks if the count correctly updated in some multi-thread scenario. But it's again indirect evidence, because it states that the count correctly updated, not that it would be incorrectly updated in other cases.
  3. And the only direct evidence will be the test with error. For that you'll need to test a version of code without AtomicLong... Well, you can do that.

But if you do understand that

the threads in the thread pool may be created in a multi-threading way thus to ensure the id of the threads not get duplicated we need the id-generator to be a atomic variable

what else do you need? The mental experiment (unlike the test from the third bullet) is quite simple:

  1. newThread is called from Thread1
  2. It gets to the point when it needs to update the count
  3. The value of the count is read, and placed in a register.
  4. The value of the count is incremented in the register but not yet written to the memory where the count is stored.
  5. At this moment the context is switched. newThread from Thread1 is paused. newThread is called again but from Thread2
  6. It gets to the point when we need to update the count
  7. Oops! The Thread2 can't read the updated value of the count from the register. It can read it from the memory, but there is still an old value.
x00
  • 13,643
  • 3
  • 16
  • 40
  • Well the "threads may be created in multi-threading way" is the one I think of to convince myself. But now I realize what I am really confusing is that what is point of creating threads in a multi-threading way? The threads that create those sub-threads is short-lived and may not be re-used efficiently. Also I am not clear if threads is created in a multi-threading way how can the thread pool still be able to manage all of them. I suppose this is a even larger topic right? – Boyu Zhang Jul 12 '20 at 07:43
  • Don't really understand what you mean by `those sub-threads is short-lived and may not be re-used efficiently`. Yes, that: `how can the thread pool still be able to manage all of them` is a larger topic :) But again I don't see what difficulties you're concerned with. And about the point of creating threads in a multi-threading way: let's say we have couple of threads reading from network, at some point they decides to log something or make some heavy calculations, and for that they need to start new threads... Who will create whose threads? Some main thread? – x00 Jul 12 '20 at 08:34
  • Yes it's possible, but not always a great idea. 1) it's more complicated 2) Pool-thread will need to receive signals from those two threads, and that's a contention – x00 Jul 12 '20 at 08:36