NB. Речь идет не о завершении рабочего потока. Это о последствиях обычного увольнения.

Я вызываю execute в EDT, так что это порождает "рабочий поток".
Я звоню worker.get() в Callable, который я отправляю в ExecutorService, в частности в ThreadPoolExecutor.

Задача завершается нормально (т.е. worker.get() возвращается с результатом, и Callable также заканчивается ОК).

Я вызываю shutdown() в Executor Service: метод завершается нормально.

Загадка возникает, когда я после этого исследую нити. Это в контексте модульного тестирования, и я в основном хочу убить все действия, которые были начаты во время последнего теста.

Я перечисляю все потоки, и в дополнение к различным, которые присутствовали до начала теста, я вижу следующее:

# поток Поток [пул-2-поток-1,5, основной], состояние ОЖИДАНИЕ, жив Верно, прервано Ложь, группа потоков main
# thread Поток [SwingWorker-pool-4-thread-1,5, main], состояние WAITING, живое True, прервано False, основная группа потоков

Проблема в том, что по мере запуска тестов вновь созданный объект «Приложение» каждый раз создает новый пул потоков, и создается все больше и больше SwingWorkers ... и ни один из этих потоков, похоже, не переходит в { {X1}} "ПРЕКРАЩЕН" (что, вероятно, означало бы, что они перестанут быть в списке).

Затем я попытался вызвать join в «SwingWorker-pool-4-thread» Thread ... но это привело к зависанию моего текущего потока.

Может ли кто-нибудь объяснить, что здесь происходит и как перевести эти потоки в "ЗАВЕРШЕННЫЙ"?

позже

Ответ на 2 полезных комментария. 10 ниток, верно. Я считаю, что тест, если он не проходит (или возникает исключение), это может оставить вещи в неприятном состоянии: предположим, в функциональном тесте задействованы 3 SW и всякие publish/process вещи происходят , не говоря уже о различных Runnable в EDT. Перед тем, как перейти к следующему тесту, я хочу, чтобы все было закрыто, закрыто, закончилось, убито, вернулось в первозданное, девственное состояние!

Чтобы гарантировать, что все Runnable закончились, у нас есть Robot.waitForIdle (). Я также придумал стратегию, чтобы убедиться, что вещь publish/process закончилась: SwingWorker, done () выполняется до завершения вызовов process ().... Но на самом деле последнее, как бы оно ни было полезно, предполагает, что тестовый код знает , на каких SW вызывать метод ... Я ищу мушкетон, который просто убьет их до смерти.

toString для этих объектов Thread ясно показывает, что было создано с момента запуска последнего теста ... но я не знаю, как их убить. Разве «ОЖИДАНИЕ» не означает, что они, как вампиры, могут снова подняться и совершить всевозможные плохие поступки? И просто оставлять огромное количество "ожидающих" потоков вокруг, потенциально достигающих их сотен, кажется немного ... неопрятным!

даже позже

Ах да, получение объекта AppContext жизненно важно, потому что вам нужно "сбросить" его, чтобы предотвратить RejectedExecutionException. См. Мой ответ о том, как это сделать в Jython.

Я также отмечаю, что в документе SW API говорится

Поскольку SwingWorker реализует Runnable, SwingWorker может быть передан исполнителю для выполнения.

Предположительно это означает, что вам вообще не нужно использовать исполнителя SW по умолчанию. Это может быть подходящим вариантом, но если в коде приложения не требуется по другой причине, это как бы нарушает принцип не адаптировать код приложения для удобства кода тестирования ...

3
mike rodent 22 Фев 2016 в 22:52

2 ответа

Лучший ответ

Отказ от ответственности: я не уверен, что вижу в этом необходимость, но ...

Покопавшись в исходном коде SwingWorker, рабочий использует метод private getWorkersExecutorService, чтобы получить ExecutorService для текущей JVM.

При этом AppContext используется для хранения экземпляра ExecutorService, из которого все экземпляры SwingWorker (для JVM) будут извлекать потоки для выполнения своих функций.

Итак, если вы сделаете что-то вроде ...

AppContext appContext = AppContext.getAppContext();
ExecutorService executorService = (ExecutorService) appContext.get(SwingWorker.class);
System.out.println(executorService);

Распечатает что-то вроде ...

java.util.concurrent.ThreadPoolExecutor@4554617c[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]

(предполагается, что вы ранее запустили SwingWorker, иначе он вернет null)

Но что это значит? Что ж, теперь мы можем получить прямой доступ к ExecutorService для всех SwingWorker (теперь может начаться апокалипсис)

Это означает, что вы можете завершить работу службы и даже удалить ее, например, из AppContext ...

SwingWorker worker = new SwingWorker() {
    @Override
    protected Object doInBackground() throws Exception {
        System.out.println("Starting");
        Thread.sleep(10000);
        System.out.println("Ending");
        return null;
    }
};
worker.execute();
synchronized (SwingWorker.class) {
    AppContext appContext = AppContext.getAppContext();
    ExecutorService executorService = (ExecutorService) appContext.get(SwingWorker.class);
    System.out.println(executorService);
    System.out.println("Shutting down");
    executorService.shutdownNow();
    try {
        System.out.println("Waiting");
        executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS);
        System.out.println("Done");
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
    appContext.remove(SwingWorker.class);
}

В моем тестировании он выводит ...

java.util.concurrent.ThreadPoolExecutor@4554617c[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
Shutting down
Waiting
Starting
Done

Так что у рабочего даже нет возможности начать. Если я помещаю задержку (после вызова execute), shutdownNow в основном забивает ее и не будет ждать завершения рабочего процесса, но если я использую shutdown, это произойдет, поэтому у тебя есть это.

Я должен добавить, AFAIK WAITING означает, что они ничего не делают. В случае SwingWorker эти потоки объединены в пул, так что да, рабочий будет пытаться повторно использовать их, если сможет, НО потому что в "обычных" операциях SwingWorker worker должен иметь дело с тем фактом, что метод doInBackground может ужасно взорваться, он уже разработан для решения этой проблемы (поэтому get выдает Exception), поэтому, если у вас нет конкретного вариант использования, я не уверен, что вам нужно так много проблем, просто говоря

5
MadProgrammer 23 Фев 2016 в 02:46

Это ответ на ответ MadProgrammer, за который я его благодарю.

Пользователи Jython могут получить доступ и использовать частные методы. Ниже, чтобы получить все потоки, а также получить ExecutorService ПО SW.

Выполнение приведенного ниже кода действительно показывает, что поток «SwingWorker ...» исчезает из списка потоков в конце из-за shutdownNow. Это не останавливает 3 новых потока, сохраняющихся в конце, «TimerQueue», «AWT-Windows» и «Java2D Disposer» ... но все они находятся в «системе» ThreadGroup, и единственная «основная» "Остающийся групповой поток - это текущий поток, поэтому я бы сказал, что проблема (если есть) решена!"

def get_all_threads():
    private_static_method = java.lang.Thread.getDeclaredMethod( 'getThreads' )
    private_static_method.accessible = True
    # invoking a static method: one param, None
    return private_static_method.invoke( None )

def show_all_threads():
    for thread in get_all_threads():
        print( '  # ID %d: thread %s, state %s, alive %s, interrupted %s, thread group %s' % ( 
            thread.id, thread, thread.state, thread.alive, thread.isInterrupted(), thread.threadGroup.name,) )
        if thread is java.lang.Thread.currentThread():
            print( '  # --> current thread' )

class Worker( javax.swing.SwingWorker ):
    def doInBackground( self ):
        print( 'Starting' )
        time.sleep( 3 )
        print( 'Ending' )

Worker().execute()

private_static_method = javax.swing.SwingWorker.getDeclaredMethod( 'getWorkersExecutorService' )
private_static_method.accessible = True
exec_serv = private_static_method.invoke( None )

show_all_threads()
exec_serv.shutdownNow()
print( '# shutDownNow ordered...' )
exec_serv.awaitTermination( java.lang.Integer.MAX_VALUE, java.util.concurrent.TimeUnit.DAYS )
print( '# ...terminated' )
show_all_threads()

# now "reset" the AppContext object so future executes won't raise a RejectedExecutionException
app_context = sun.awt.AppContext.getAppContext()
app_context.remove( javax.swing.SwingWorker )
1
mike rodent 24 Фев 2016 в 07:52