Search code examples
javamultithreadingthreadpoolexecutormanagedthreadfactory

java Guava ThreadFactoryBuilder why we need the count as AtomicLong


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?


Solution

  • 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.