Поток Java: «присоединиться» заморозил мою программу - PullRequest
1 голос
/ 07 октября 2009

Моя программа выглядела так:

class Prog
 {
 BufferedImage offscreen;
 KindOfDatabase db;
 MyThread thread;

 class MyThread extends Thread
    {
    volatile boolean abort=false;
    long lastUpdated;
    public void run()
        {
        try
          {
          KindOfCursor c = db.iterator();
          while(c.getNext())
            {
            if(abort) break;
            //fill a histogram with the data,
            // calls SwingUtilities.invokeAndWait every 500ms to
            //do something with offscreen and update a JPanel
            }
          catch(Exception err)
            {
            err.printStackTrace();
            }
          finally
            {
            c.close();
            }
        }
    }

  void stopThread()
       {
       if(thread!=null)
          {
          thread.abort=true;
          thread.join();
          thread=null;
          }
       }
  void startThread()
      {
      stopThread();
      thread=new MyThread();
      thread.start();
      }
(....)
 }

1) Программа хорошо работала на моем компьютере. Но когда я запустил его, установил соединение 'ssh -X remote.host.org' , все было очень медленно, и программа была заморожена при вызове thread.join (). Я заменил «join» на «interrupt ()», и программа больше не была заморожена. Зачем ? Стоит ли опасаться, что при вызове interrupt () оператор 'finally', закрывающий итератор, не был вызван?

2) я должен использовать Thread.isInterrupted () вместо моего логического «abort»?

Спасибо

ОБНОВЛЕНИЕ : мой флаг прерывания был помечен как volatile. Не меняет замороженное состояние.

Ответы [ 4 ]

6 голосов
/ 07 октября 2009

Thread.join предназначен для "заморозки" вашей темы!

Когда вы вызываете объединение, текущий поток приостанавливается до тех пор, пока не завершится поток, к которому он присоединяется. В вашем случае это зависание происходит потому, что экземпляр MyThread не завершается своевременно.

Одна вещь, которая может вас здесь кусать - вам нужно объявить переменную прерывания как volatile, чтобы изменения были надежно видны другим потокам . Поскольку вы этого не делаете, ваш MyThread может полностью увидеть кэшированную версию переменной abort, где она всегда истинна. Вот краткое описание этого здесь .

Редактировать: Я пропустил ваше заявление о том, что оно работает локально, но не на удаленной машине. На самом деле это не так уж редко с гоночными условиями, связанными с параллелизмом, поскольку они могут проявляться или не проявляться в зависимости от различных факторов, таких как настройка оборудования, нагрузка на компьютер и т. Д. В этом случае, например, если на вашем локальном компьютере есть только один физический процессор / ядро, тогда ваш ошибочный код будет вероятно работать нормально; там только один кэш ЦП, поэтому другой поток, скорее всего, «увидит» основной поток, изменяющий флаг abort, хотя он явно не помечен как энергозависимый. Теперь перенесите это на многоядерный компьютер, и если потоки будут распределены по отдельным ядрам, они будут использовать отдельные кэши и вдруг не увидят изменений в энергонезависимых кэшированных переменных.

Вот почему очень важно понимать последствия параллелизма и именно то, что гарантировано, потому что сбои не проявляются согласованным образом.

Обновление реакция: если это все еще не работает, когда прерывание изменчиво, это звучит так же, как MyThread не проверяет переменную достаточно часто. Имейте в виду, что он только «заметит», что флаг прерывания был установлен непосредственно после вытягивания другого вида строки из вашего вида курсора. Если возможно, что обработка одного элемента относительно гистограммы может занять много времени, тогда, конечно, он не увидит флаг в течение этого времени.

Возможно, вам просто нужно чаще проверять флаг отмены. Вы говорите, что звоните invokeAndWait каждые 500 мс; Вы должны проверять свой флаг прерывания перед каждым вызовом, поэтому вам нужно подождать не более 500 мсек, чтобы поток завершился! Просмотрите эту часть своего кода и посмотрите, есть ли какие-либо внутренние циклы, которые вы можете изменить, чтобы они выглядели как while (... && !abort).

Другим перпендикулярным подходом является также начало прерывания потока. В частности, SwingUtilities.invokeAndWait является прерываемым, поэтому если вы вызываете thread.interrupt() в своем методе stopThread(), то вызов invokeAndWait завершится очень быстро (с помощью исключения InterruptedException), вместо того, чтобы ждать, пока он завершится нормально, прежде чем ваш код получит возможность проверить флаг отмены снова. Это дает дополнительное преимущество: если для выполнения какой-либо другой прерываемой операции требуется очень много времени, она также сразу же возвращается. (В этом случае вы, вероятно, захотите явно перехватить InterruptedException в своем бите обработки кода; вам не нужно ничего делать для его обработки как такового, просто примите это как знак, чтобы проснуться и снова проверить флаг Прочитайте эту превосходную статью Developerworks для получения дополнительной информации об обработке InterruptedExceptions).

Наконец, если у вас все еще есть проблемы, то вам поможет отладка старой доброй печати. Если у вас будет MyThread печать на консоль (или, возможно, какой-нибудь файл журнала) каждый раз, когда он проверяет флаг отмены, вы сможете увидеть, не связана ли проблема с зависанием самого MyThread; так как в этом случае он никогда не завершится и вызывающий поток никогда не вернется из вызова join (). Перемещение проверки флага прерывания вниз вниз, вероятно, поможет в этом.

4 голосов
/ 07 октября 2009

Вы разделили данные между двумя потоками без каких-либо барьеров памяти. Возможно, что если ваш основной поток установит abort = true, он установит abort локально, но другой процессор уже имеет «false» в своем локальном кэше для этого поля.

Для этой цели используется ключевое слово volatile.

Это может работать на вашем компьютере, потому что у вас один процессор, а на удаленном компьютере - нет.

1 голос
/ 07 октября 2009

Что делает ваш KindOfCursor c.next()? Это блокирование и ожидание большего количества данных? Если это так, его прерывание могло привести к тому, что он перестал ждать и быстро вернулся.

0 голосов
/ 20 октября 2012

почему бы вам не попробовать отладить его? когда вы выполняете команду thread.join () в главном потоке, она ожидает завершения потока. Если ваша программа остановлена ​​после этой команды, возможно, поток не завершен. Причиной может быть то, что вы не можете подключиться к БД или вы слишком медленно получаете данные из БД (через удаленный хост) Я думаю, что вы должны использовать log4j (или что-то может отлаживать) для его отладки.

Подсказка: вы не должны использовать переменную boolean abort для управления потоком. Это небезопасно, если вы применяете в многопоточной среде. Вы пытаетесь заменить на этот код:

...in body thread
void run(){
    ...
    while(condition){
      if (interrupted()) 
          break;
    .....
}


...in body Prog class
void stopThread(){
    if(thread!=null)
    {
      thread.interrupt();
      thread.join();// i think you do not need this line, try it if you call interupt
      thread=null;
     }
}

Надеюсь, вы решите это в ближайшее время.

...