ThreadPoolExecutor getActiveCount()

У меня есть ThreadPoolExecutor, который, кажется, врет мне, когда я вызываю getActiveCount(). Однако я не сделал много многопоточного программирования, поэтому, возможно, я что-то делаю неправильно.

Здесь мой TPE

@Override
public void afterPropertiesSet() throws Exception {

    BlockingQueue<Runnable> workQueue;
    int maxQueueLength = threadPoolConfiguration.getMaximumQueueLength();
    if (maxQueueLength == 0) {
        workQueue = new LinkedBlockingQueue<Runnable>();
    } else {
        workQueue = new LinkedBlockingQueue<Runnable>(maxQueueLength);
    }

    pool = new ThreadPoolExecutor(
                   threadPoolConfiguration.getCorePoolSize(),
                   threadPoolConfiguration.getMaximumPoolSize(),
                   threadPoolConfiguration.getKeepAliveTime(),
                   TimeUnit.valueOf(threadPoolConfiguration.getTimeUnit()),
                   workQueue,
                   // Default thread factory creates normal-priority,
                   // non-daemon threads.
                   Executors.defaultThreadFactory(),
                   // Run any rejected task directly in the calling thread.
                   // In this way no records will be lost due to rejection
                   // however, no records will be added to the workQueue
                   // while the calling thread is processing a Task, so set
                   // your queue-size appropriately.
                   //
                   // This also means MaxThreadCount+1 tasks may run
                   // concurrently. If you REALLY want a max of MaxThreadCount
                   // threads don't use this.
                   new ThreadPoolExecutor.CallerRunsPolicy());
}

В этом классе у меня также есть DAO, который я передаю в свой Runnable (FooWorker), например:

@Override
public void addTask(FooRecord record) {
    if (pool == null) {
        throw new FooException(ERROR_THREAD_POOL_CONFIGURATION_NOT_SET);
    }
    pool.execute(new FooWorker(context, calculator, dao, record));
}

FooWorker запускает record (единственный не одиночный) через конечный автомат через calculator, затем отправляет переходы в базу данных через dao, например:

public void run() {
    calculator.calculate(record);
    dao.save(record);
}

Как только мой основной поток будет создан, создавая новые задачи, я пытаюсь и жду, чтобы убедиться, что все потоки успешно завершены:

while (pool.getActiveCount() > 0) {
    recordHandler.awaitTermination(terminationTimeout, 
                                   terminationTimeoutUnit);
}

То, что я вижу из выходных журналов (которые предположительно ненадежны из-за потоковой передачи), заключается в том, что getActiveCount() возвращает слишком рано слишком рано, а цикл while() выходит, а мои последние потоки все еще печатают вывод из calculator.

Примечание. Я также попробовал вызов pool.shutdown(), затем используя awaitTermination, но затем в следующий раз, когда моя работа запущена, пул все еще отключается.

Мое единственное предположение заключается в том, что внутри потока, когда я отправляю данные в dao (так как это singleton, созданный Spring в основном потоке...), java рассматривает поток неактивным, поскольку (я предполагаю ) он обрабатывает/ждет в основном потоке.

Интуитивно, основываясь только на том, что я вижу, это моя догадка. Но... Это действительно то, что происходит? Есть ли способ "сделать это правильно", не добавляя ручную добавленную переменную вверху run() и уменьшаясь в конце для отслеживания количества потоков?

Если ответ "не проходит в дао", то разве мне не нужно "новое" DAO для каждого потока? Мой процесс уже (красивый, эффективный) зверь, но это действительно сосать.

Ответ 1

Как JavaDoc из getActiveCount указывает, это приблизительное значение: вы не должны основывать на нем какие-либо основные бизнес-логические решения.

Если вы хотите дождаться завершения всех запланированных задач, вы должны просто использовать

pool.shutdown();
pool.awaitTermination(terminationTimeout, terminationTimeoutUnit);

Если вам нужно дождаться завершения конкретной задачи, вы должны использовать submit() вместо execute(), а затем проверить < объект href= "http://download.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html" rel= "noreferrer" > Future для завершения (либо используя isDone(), если вы хотите сделать это без блокировки или просто называть get(), который блокируется до выполнения задачи).

Ответ 2

Документация предполагает, что метод getActiveCount() on ThreadPoolExecutor не является точным числом:

getActiveCount

public int getActiveCount()

Возвращает приблизительное количество потоков, которые активно выполняют задачи.

Возвращает: количество потоков

Лично, когда я выполняю многопоточную работу, такую ​​как this, я использую переменную, которую я увеличиваю при добавлении задач, и уменьшаю, когда я получаю их вывод.