java Guava ThreadFactoryBuilder, зачем нам счетчик как AtomicLong - PullRequest
1 голос
/ 09 июля 2020
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;
      }
    };
  }

Недавно я начал изучать ThreadFactory, который используется ThreadPoolExecutor для создания новых потоков в пуле потоков. Для удобства отладки и мониторинга мы не хотим, чтобы потоки, созданные пулом потоков, имели значение по умолчанию 0,1,2,3, а должны иметь осмысленное имя.

Одним из способов достижения этой цели является для реализации настраиваемого ThreadLoad, который может устанавливать имя потока при создании потока. Guava имеет удобный класс конструктора для настройки ThreadFactory, и я хочу sh учиться на нем.

Нетрудно понять большую часть этого класса, но я вполне смущает переменная count в методе doBuild.

Я также перешел к исходному коду ThreadPoolExecutor#Worker, где фактически вызывается newThread() из ThreadFactory.

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

Но я все еще не понимаю, зачем нам здесь нужна переменная atomi c.

Конечно, я могу предположить, что потоки в пуле потоков могут быть созданы многопоточным способом, чтобы гарантировать идентификатор потоков не дублируется, нам нужно, чтобы генератор идентификаторов был переменной atomi c, но у меня пока нет прямых доказательств этого предположения.

Может ли кто-нибудь пролить свет на это?

1 Ответ

1 голос
/ 11 июля 2020

Я сомневаюсь, что вы найдете в коде какие-либо

прямые доказательства

. Есть только 3 возможности:

  1. Комментарий в коде от автора, объясняющий, что AtomicLong используется по соображениям безопасности потоков. Но это все еще косвенное свидетельство, потому что автор может ошибаться в своих предположениях (а это не так).
  2. Тест, который проверяет, правильно ли обновляется count в каком-то многопоточном сценарии. Но это снова косвенное свидетельство, потому что в нем говорится, что count правильно обновил , а не что неправильно обновил в других случаях.
  3. И единственный прямой Доказательством будет тест с ошибкой . Для этого вам нужно протестировать версию кода без AtomicLong ... Что ж, вы можете это сделать.

Но если вы понимаете, что

потоки в пуле потоков могут быть созданы многопоточным способом, таким образом, чтобы идентификатор потоков не дублировался, нам нужно, чтобы генератор идентификаторов был atomi c переменной

что еще нужно? Мысленный эксперимент (в отличие от теста из третьей пули) довольно прост:

  1. newThread вызывается из Thread1
  2. Доходит до того, что нужно обновить count
  3. Значение из count считывается и помещается в регистр.
  4. значение из count увеличивается в регистре , но еще не записывается в память , где хранится count.
  5. В этот момент происходит переключение контекста. newThread из Thread1 приостановлено. newThread вызывается снова, но с Thread2
  6. Он доходит до того момента, когда нам нужно обновить count
  7. Упс! Thread2 не может прочитать обновленное значение count из регистра. Он может считывать его из памяти, но все еще остается старое значение .
...