2016-01-05 4 views
0

Я провел некоторое исследование с моей проблемой и по-прежнему не могу решить проблему. Threading для меня новичок, и у меня проблемы с пониманием. В моей программе я начинаю поток, который передает файлы в определенный промежуток времени. SWT используется для GUI в этой программе. В моем главном коде пользовательского интерфейса у меня есть кнопка паузы и воспроизведения. Кнопка воспроизведения и связанной с ними код:IllegalMonitorStateException при попытке ждать() поток

playButton.addSelectionListener(new SelectionAdapter() { 
     @Override 
     public void widgetSelected(SelectionEvent e) { 

      if(isRunning){ 
       // TODO implies runningThread is waiting, notify it 
      }else{ 
       playButton.setEnabled(false); 
       pauseButton.setEnabled(true); 
       try { 
        play(); 
       } catch (IOException e1) { 
        e1.printStackTrace(); 
       } 
      } 
     } 
    }); 

public void play() throws IOException{ 

    if(controller.timMan.getEventSendPreferences().equalsIgnoreCase("manual")){ 
     isRunning = true; 
     manualThread.start(); 


    }else if(controller.timMan.getEventSendPreferences().equalsIgnoreCase("timed")){ 
     isRunning = true; 
     timerThread.start(); 
    } 

    return; 
} 

timerThread реализован как таковой:

timerThread = new Thread(new RunOnTimer(controller)); 

public static class RunOnTimer implements Runnable{ 

    ScriptController controller; 

    public RunOnTimer(ScriptController c){ 
     controller = c; 
    }; 

    @Override 
    public void run(){ 
     try{ 
      synchronized(this){ 
       controller.runOnTimer(); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
}; 

и здесь функция runOnTimer() вызывается в перспективе:

public void runOnTimer() throws IOException{ 

    for(File f : dirMan.getEventFileList()){ 
     int randomTimeValue = 0; 
      int range = timMan.getUpperTimerBound() - timMan.getLowerTimerBound(); 
      if(range > 0) 
       randomTimeValue = (new Random().nextInt(timMan.getUpperTimerBound() - timMan.getLowerTimerBound()) + 0); 

      try { 
       Thread.sleep(1000 * (randomTimeValue + timMan.getLowerTimerBound())); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      dirMan.updateFileCounts(); 
      System.out.println("Event " + dirMan.getSentEventFiles()); 
      File dest = new File(dirMan.getDestinationFolder() + "\\" + f.getName()); 
      Files.copy(f.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING); 
    } 
    updateFileCounts(); 
    return; 
} 

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

pauseButton.addSelectionListener(new SelectionAdapter() { 

     @Override 
     public void widgetSelected(SelectionEvent e) { 
      if(timerThread.isAlive()){ 
       try { 
        timerThread.wait(); 
       } catch (InterruptedException e1) { 
        // TODO Auto-generated catch block 
        e1.printStackTrace(); 
       } 
      } 

      pauseButton.setEnabled(false); 
      playButton.setEnabled(true); 
     } 

    }); 

В других вопросах, которые я прочитал, и от того, что результатов показал Google, синхронизация, как правило, проблема, когда речь заходит об этой ошибке. Что-то связано с получением владельца монитора объектов. Опять же, я не работал с потоками, поэтому эта концепция объектных мониторов для меня загадка. Итак, после прочтения этих вопросов я попытался использовать синхронизацию в методе run() класса RunOnTimer, но, похоже, ничего не изменил при попытке «приостановить» (т. Е. Заставить поток ждать).

Что мне недостает или что-то не так. Программа будет работать нормально, и поток будет работать, как я ожидал, за исключением ошибки, конечно.

+0

Не могли бы вы предоставить трассировку стека ошибки? Кроме того, вам не нужен вызов 'return' в конце методов void. – RamV13

ответ

0

Если вы читаете документацию для Object.wait вы увидите эту важную часть информации:

Текущий поток должен владеть монитором данного объекта.

Это означает, что для объекта, для которого вы звоните, wait, вы должны иметь синхронизированный блок. Документация, на самом деле, имеет хороший псевдокод, который показывает правильное использование wait

В вашем случае:

synchronized(timerThread) 
{ 
    while(shouldStillWait()) 
    { 
    timerThread.wait(); 
    } 
} 

boolean shouldStillWait() 
{ 
    ... 
} 

Также должно быть место где-то в коде, который вызывает timerThread.notify(), без него вы будете ждать навсегда.

Тем не менее, начиная с Java 5, есть намного лучшие встроенные примитивы синхронизации, которые используют wait/notify в значительной степени в прошлом.

И это ХОРОШЕЕ, потому что wait/notify очень хрупкое и подверженное ошибкам.

Я предлагаю прочитать отличный 'Java Concurrency in Practice' Брайан Гетц, чтобы ознакомиться с новыми примитивами синхронизации и хорошим многопоточным программированием.

+0

Где я могу добавить это утверждение? В прослушивателе кнопок паузы или методе, который кнопка вызывает для запуска потока? Я боюсь, что цикл while заблокирует UI. –

0

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

Итак, для достижения желаемой функциональности у вас должна быть переменная для поддержания состояния приложения (т. Е. Приостановлено или нет).

В вашем главном классе приложения вы можете объявить boolean переменную

private boolean paused; 

Внутри вашей кнопки паузы метод обработки событий, вы должны удалить

 if(timerThread.isAlive()){ 
      try { 
       timerThread.wait(); 
      } catch (InterruptedException e1) { 
       // TODO Auto-generated catch block 
       e1.printStackTrace(); 
      } 
     } 

и заменить его

paused = !paused; // to just toggle the state 
timerThread.notifyAll(); // to notify waiting thread 

Тогда в вашем Runnable будет

@Override 
public void run(){ 
    try{ 
     while (paused) { 
      synchronized(this) { 
       wait(); // keep sleeping this thread while the state is still paused 
      } 
     } 
     controller.runOnTimer(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 
+0

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

+0

Я отредактировал ответ соответственно. Надеюсь, это поможет! – RamV13

Смежные вопросы