Java 尝试等待()线程时出现IllegalMonitorStateException

Java 尝试等待()线程时出现IllegalMonitorStateException,java,multithreading,Java,Multithreading,我已经对我的问题做了一些研究,但仍然无法解决手头的问题。线程对我来说是新的,所以我很难理解。在我的程序中,我启动了一个线程,该线程在一段时间内传输文件。SWT用于此程序中的GUI。在我的主UI代码中,我有一个暂停和播放按钮。播放按钮和相关代码: playButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEven

我已经对我的问题做了一些研究,但仍然无法解决手头的问题。线程对我来说是新的,所以我很难理解。在我的程序中,我启动了一个线程,该线程在一段时间内传输文件。SWT用于此程序中的GUI。在我的主UI代码中,我有一个暂停和播放按钮。播放按钮和相关代码:

    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();
        }
    }
};
下面是在run中调用的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);
        }

    });
在我读到的其他问题中,从谷歌的搜索结果来看,当出现这个错误时,同步通常是个问题。与获取对象监视器的所有者有关。同样,我没有使用线程,所以对象监视器的概念对我来说是个谜。 因此,在阅读了这些之后,我尝试在RunOnTimer类的run()方法中使用synchronized,但在尝试“暂停”(即让线程等待)时,似乎没有改变任何东西


我错过了什么或做错了什么。程序将运行良好,线程将像我预期的那样运行,当然除了错误

如果您阅读文档,您将看到以下关键信息:

当前线程必须拥有此对象的监视器

这意味着您必须在为其调用
wait
的对象上有一个同步块。实际上,文档中有一个很好的伪代码,显示了
wait

就你而言:

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

boolean shouldStillWait( )
{
   ...
}
此外,代码中应该有一个地方调用
timerThread.notify()
,没有它,您将永远等待

这就是说,自从Java5以来,有了更好的内置同步原语,它们利用了几乎所有过去的
wait/notify

这是一件好事,因为
wait/notify
非常脆弱且容易出错


我建议阅读Brian Goetz的《优秀》,以熟悉新的同步原语和良好风格的多线程编程。

问题是您无法调用
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();
    }
}

您能提供错误的堆栈跟踪吗?另外,在void方法的末尾不需要调用
return
。但问题是,即使我实现了上面所示的方法,它也不会像我打算的那样暂停线程。我相应地编辑了答案。希望这有帮助!但是,我应该在哪里添加此声明?在pause buttons侦听器中,还是在按钮调用的方法中启动线程?我担心while循环会锁定UI。