Helpers - компьютеры, интернет, программирование

Java ExecutorService приостанавливает/возобновляет определенный поток

Есть ли способ использовать ExecutorService для приостановки/возобновления определенного потока?

private static ExecutorService threadpool = Executors.newFixedThreadPool(5);

Представьте, что я хочу остановить поток с идентификатором = 0 (при условии, что каждому из них назначается инкрементный идентификатор до тех пор, пока не будет достигнут размер пула потоков).

Через некоторое время, скажем, нажав кнопку, я хочу возобновить этот конкретный поток и оставить все остальные потоки с их текущим статусом, который можно приостановить или возобновить.

Я нашел в документации по Java незавершенную версию PausableThreadPoolExecutor. Но это не соответствует тому, что мне нужно, потому что возобновляет все потоки в пуле.

Если нет способа сделать это с реализацией ExecutorService по умолчанию, может ли кто-нибудь указать мне на реализацию Java для этой проблемы?


  • Уверены ли вы? Поток с заданным идентификатором выполняет какое-то случайно выбранное задание, поэтому вы останавливаете какое-то неизвестное задание. Какой смысл так делать? 11.08.2012
  • Представьте себе следующую ситуацию: у вас есть менеджер загрузок, и в этом менеджере вы можете останавливать и возобновлять загрузки. Для каждого, что вы загружаете, не имеет значения, что не имеет значения, и вы хотите остановить эту загрузку, чтобы возобновить ее в любом месте. Это делает вопрос более ясным для вас? 11.08.2012
  • Более понятно, но все равно не так много смысла. Остановка/возобновление загрузки относится к бизнес-логике, а пул потоков — к вычислительным ресурсам. Имхо, управлять логикой путем добавления/удаления ресурсов - плохая идея. 12.08.2012

Ответы:


1

Вы на ложном пути. Пул потоков владеет потоками, и совместное использование их с вашим кодом может привести к беспорядку.
Вам следует сосредоточиться на создании ваших задач (переданных в потоки с возможностью отмены/прерывания) и не взаимодействовать с потоками. принадлежит пулу напрямую.
Кроме того, вы не будете знать, какое задание выполняется в то время, когда вы пытаетесь прервать поток, поэтому я не понимаю, почему вам может быть интересно это сделать.

Обновление:
Правильный способ отменить вашу задачу, отправленную в пул потоков, — через Future для задачи, возвращенной исполнителем.
1) Таким образом, вы точно знаете, что задача вы на самом деле стремитесь к отмене
2)Если ваши задачи уже спроектированы так, чтобы их можно было отменить, значит, вы уже на полпути
3) Не используйте флаг для обозначения отмены, а вместо этого используйте Thread.currentThread().interrupt()

Обновление:

public class InterruptableTasks {  

    private static class InterruptableTask implements Runnable{  
        Object o = new Object();  
        private volatile boolean suspended = false;  

        public void suspend(){          
            suspended = true;  
        }  

        public void resume(){       
            suspended = false;  
            synchronized (o) {  
                o.notifyAll();  
            }  
        }  


        @Override  
        public void run() {  

            while(!Thread.currentThread().isInterrupted()){  
                if(!suspended){  
                    //Do work here      
                }
                else{  
                    //Has been suspended  
                    try {                   
                        while(suspended){  
                            synchronized(o){  
                                o.wait();  
                            }                           
                        }                       
                    }  
                    catch (InterruptedException e) {                    
                    }             
                }                           
            }  
            System.out.println("Cancelled");        
        }

    }

    /**  
     * @param args  
     * @throws InterruptedException   
     */  
    public static void main(String[] args) throws InterruptedException {  
        ExecutorService threadPool = Executors.newCachedThreadPool();  
        InterruptableTask task = new InterruptableTask();  
        Map<Integer, InterruptableTask> tasks = new HashMap<Integer, InterruptableTask>();  
        tasks.put(1, task);  
        //add the tasks and their ids

        Future<?> f = threadPool.submit(task);  
        TimeUnit.SECONDS.sleep(2);  
        InterruptableTask theTask = tasks.get(1);//get task by id
        theTask.suspend();  
        TimeUnit.SECONDS.sleep(2);  
        theTask.resume();  
        TimeUnit.SECONDS.sleep(4);                
        threadPool.shutdownNow();      
    }
11.08.2012
  • Можете ли вы быть более конкретным и предоставить мне реальную реализацию или пример? Я действительно оценил это, потому что я борюсь с этой проблемой довольно долгое время. 11.08.2012
  • @RicardoSantos: Но почему бы вам не сделать свои задачи отменяемыми? Не рекомендуется взаимодействовать с потоками, которыми ваш код не владеет напрямую. 11.08.2012
  • Я согласен с рекомендацией сделать задачи отменяемыми. ОП написал, что я хочу остановить поток с идентификатором = 0. Это не имеет особого смысла, потому что в любой момент времени вы не знаете, что делает поток 0. Но имеет смысл говорить об остановке какой-то работы, выполняемой в данный момент, отсюда и рекомендация об отмене задач. 11.08.2012
  • На самом деле мои темы используют флаг для отмены. В моей итерации цикла while я использую этот флаг, пока не установлю для него значение false. Так что да, мои Runnables можно отменить. Проблема в том, что если я устанавливаю флаг в false и останавливаю выполнение, проверяя состояние потоков, я вижу, что мой поток находится в состоянии ожидания. Что как-то круто. Теперь проблема, которая у меня возникла, заключается в том, как возобновить их работу? И, пожалуйста, не отвечайте, устанавливая флаг в предыдущее состояние, потому что это не работает. 11.08.2012
  • @ user384706 выглядит именно так, как я искал, за исключением одной части. Вы уведомляете все потоки, что означает, что если у меня приостановлено 2 задачи (id = 1 и id = 2) и вы хотите возобновить задачу с id = 2, вы уведомляете все потоки (id = 1 и id = 2) . Я прав? 12.08.2012
  • @RicardoSantos: каждый поток будет выполняться сам по себе InterruptableTask, и, поскольку они не являются общими, lock является частным для каждого потока. 12.08.2012
  • @RicardoSantos: я имею в виду, что o, действующий как lock, не распределяется между потоками. 12.08.2012
  • @RicardoSantos: Единственная ловушка здесь заключается в том, что если вы покинете поток suspended, а затем попытаетесь завершить работу приложения, JVM не остановится, поскольку поток заблокирован в wait. Вы должны либо сделать потоки демонами, либо реализовать то, что вам действительно нужно. внимательно 12.08.2012
  • @ user384706 еще один вопрос. В заявлении else, почему вы используете это при приостановленном флаге? o.wait() из того, что я видел, блокируется, поэтому я думаю, что вам это не нужно. Я провел несколько тестов с вашей реализацией, и все работает нормально даже без этого. Поэтому я думаю, что могу закрыть этот вопрос и отметить ваш ответ как лучший или, по крайней мере, тот, который соответствует моим потребностям. Еще раз спасибо. Обновление: Да. Я видел это! Я пытаюсь оставить один поток в режиме приостановки и вызвать завершение работы, а JVM осталась работать. Я тоже заметил эту проблему. 12.08.2012
  • @RicardoSantos:Вы MUST всегда вызываете wait внутри цикла while, чтобы избежать ложных пробуждений docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait() 12.08.2012
  • Спасибо за совет. Я заметил, что если вы вызовете threadPool.shutdownNow();, все потоки, находящиеся в wait(), будут прерваны. Так почему же JVM не останавливает выполнение при вызове shutdownNow()? 12.08.2012
  • @RicardoSantos:shutdown изящно закрывает пул потоков, оставляя текущие запущенные задачи для выполнения, а ожидающие задачи могут быть завершены. shutdownNow отменяет все (возвращает список всех незапущенных задач) и резко выключается 13.08.2012
  • @Cratylus: сообщение довольно старое, но есть некоторые проблемы с кодом, который вы здесь показываете. Перехват прерывания сбрасывает флаг прерывания Threads, поэтому условие цикла while остается истинным после этого. shutdownNow ничего не отменяет, он только пытается это сделать, но не гарантирует, что что-то отключит. Если задачу нельзя прервать, она будет продолжать выполняться. 04.09.2019

  • 2

    Предложение: аналогично или вместо используемых вами флагов создайте ссылку семафор с 1 разрешением (new Semaphore(1)) для каждой задачи, которую необходимо приостановить/возобновить. В начале рабочего цикла задачи поставьте такой код:

    semaphore.acquire();
    semaphore.release();
    

    Это приводит к тому, что задача получает разрешение на использование семафора и немедленно освобождает его. Теперь, если вы хотите приостановить поток (например, нажата кнопка), вызовите semaphore.acquire() из другого потока. Поскольку у семафора сейчас 0 разрешений, ваш рабочий поток приостановится в начале следующего цикла и будет ждать, пока вы не вызовете semaphore.release() из другого потока.

    (Метод acquire() выдает InterruptedException, если ваш рабочий поток прерывается во время ожидания. Существует еще один метод acquireUninterruptibly(), который также пытается получить разрешение, но не прерывается.)

    11.08.2012
  • Конечно, это работает, но больше похоже на взлом, и это лучший подход, чем ничего не делать. Так что спасибо за ответ. Я протестирую ваш подход и @user384706 и посмотрю, какой из них имеет лучшую производительность. 12.08.2012

  • 3

    Одним из сценариев может быть то, что нужно смоделировать несколько устройств. У устройств есть функции. В целом этот набор устройств работает одновременно. И теперь, если поток представляет устройство (или один поток для одной функции устройства), можно захотеть контролировать жизненный цикл устройства, например start(), shutdown(), resume()

    11.02.2020
  • Если вы используете пул, то потоки взаимозаменяемы и не сопоставляются с устройствами. Лучший совет — сделать задачи отменяемыми и не управлять потоками на микроуровне. 11.02.2020
  • Новые материалы

    Интуитивное понимание тензоров в машинном обучении
    Тензор является важной концепцией во многих научных областях, таких как математика, физика, обработка сигналов и компьютерное зрение, и это лишь некоторые из них. В математике тензор — это..

    Использование машинного обучения для диагностики болезни Альцгеймера, часть 4
    Маркеры семантической согласованности для ранней диагностики болезни Альцгеймера (arXiv) Автор: Давиде Колла , Маттео Дельсанто , Марко Агосто , Бенедетто Витиелло , Даниэле Паоло Радичони..

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

    Анимированный математический анализ
    Использование Manim для создания математических анимированных визуализаций Визуализация данных помогает понять скрытые закономерности в данных, которые невозможно визуализировать..

    Создание простого слайдера изображений с помощью JavaScript
    Узнайте, как создать базовый слайдер изображений с помощью HTML, CSS и JavaScript. Введение В этом уроке мы создадим удобный слайдер изображений, используя JavaScript, HTML и CSS. Ползунок..

    Создание базы данных с помощью супергероя «Python»
    В этом посте мы узнаем, как создать «базу данных SQLite с помощью модуля python sqlite3, создав простую функцию входа и регистрации. Готовы ли вы к этому путешествию? Если да , давайте приступим..

    ИИ для чайников: руководство для начинающих по пониманию будущего технологий
    Вы чувствуете, что остались позади в мире ИИ? Не волнуйтесь, вы не одиноки! Со всей этой шумихой вокруг искусственного интеллекта может быть трудно понять, с чего начать. Но не позволяйте сленгу..