是否有可能等待不在Threads中的方法在Java中完成?

时间:2009-03-15 00:11:12

标签: java multithreading concurrency

我有一个对象进行一些计算,然后对于每次迭代,我想绘制发生的事情。当绘图正在发生时,我希望它等待。

这就是我所做的,基本上是:

synchronized public void compute()
{
    other.mark(variable);
    try
    {
        wait();
    }
    catch(InterruptedException e)
    {
    }
}

在OtherClass中,我有

synchronized public void mark(int var)
{
    //change some stuff
    repaint();
    notify();
}

会发生什么是compute()永远等待。我认为这会起作用,因为编译器没有给出任何错误。这两个类都没有实现Runnable或者扩展Thread,所以也许这就是问题所在?我不确定,因为我认为如果这些物体不能使用这些方法我会被警告。

我认为这可能是关于程序本身逻辑的错误,但简而言之就是我所拥有的。

7 个答案:

答案 0 :(得分:2)

您的问题表明您希望执行一些操作,以便在GUI完成时更新GUI状态或通知GUI其进度。这就是SwingWorker的目的。两个案例的链接javadoc都有一些例子。

答案 1 :(得分:1)

这根本不像您认为的那样有效。来自wait()方法的Javadoc(强调我的):

  

导致当前线程等待,直到另一个线程为此对象调用notify()方法或notifyAll()方法。

你的程序中显然没有其他线程可以唤醒睡眠compute()方法。

要解决您的特定问题,您要么必须有两个线程,要么实现compute()方法作为可恢复的方法,在伪Java中是这样的:

ComputeStatus status = new ComputeStatus();
do {
    compute(status);  // compute iteration
    mark(status);     // draw iteration
    status.next();    // next iteration
} while (!status.isFinished());

此处ComputeStatus保持当前的计算状态,comupte()知道如何从该状态继续计算。无论您是在compute()还是在主循环中更改状态,都取决于您和您正在解决的问题。

答案 2 :(得分:0)

Object.wait()和Object.notify()仅供线程使用。在您显示的代码中,方法标记(int var)在完成之前不会返回,无需等待。此外,仅在多线程程序中需要同步方法。

您的代码应为:

public void compute()
{
    other.mark(variable);
}

public void mark(int var)
{
    //change some stuff
    repaint();
}

答案 3 :(得分:0)

由于你的程序是一个GUI程序(我通过repaint()调用收集),它本身就是多线程的,即使你不知道它。 (如果没有,它会表现得非常糟糕。)

如果您不使用线程,则不能使用wait / notify或任何其他类型的同步,因为没有两个线程可以同步。但是,您不必在GUI程序中明确地使用Threads来最终使用Threads。

请注意,如果您使用依赖于线程但实际上不以任何方式使用线程的方法,Java编译器将不会警告您。

您遇到以下问题之一:

1)您正在使用线程而不知道它并且您正在使用两个不同的监视器对象。因此,当您调用notify()时,它会通知监视器该对象,但不会通知您调用wait()的第一个对象。有许多可能的解决方案。最简单的方法之一是使用JDK 5并发实用程序来执行此操作,这比内置的基本监视器等待/通知方法更好。或者,

2)您正在一个线程中运行,并且wait / notify不好。在单线程程序中等待另一个线程通知是没有意义的 - 没有其他线程可以这样做。

假设您实际上使用的是多个Thread,那么使用Java 5及更高版本解决此问题的好方法是在包含mark()的类中使用semaphore,并简化一点:

private final Semaphore sem = new Semaphore(1);

public void mark(int var) {
  sem.acquire();
  //change some stuff
  repaint();
  sem.release();
}

waitForSemaphore() {
  sem.acquire();
}

然后在compute中,当您希望等待waitForSemaphore()通知时,请致电mark()。由于mark()已经获得了信号量,因此您必须等待mark()释放信号量,然后才能通过调用waitForSemaphore()来获取信号。

答案 4 :(得分:0)

重绘方法注册了绘制组件的需要,但实际上并没有绘制它,但是Java会在下一次获得它时重新绘制对象。如果你想制作类似动画的东西,那么就没有目的等待重画完成。相反,我建议你使用计时器。现在你有两个计时器选项。如果你正在更新时间不必确切的东西,那么javax.swing.Timer通常就是你要找的东西。你这样使用它:

//imports (before class definition)
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

//put this code where you want to start doing calculations
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
    public void actionPerformed(ActionEvent evt) {
        //update your calculations
        model.calculate();
        //tell Java to call paint at the next chance it gets
        viewer.repaint();
    }
};
new Timer(delay, taskPerformer).start();

在上面的代码中,模型是您要执行计算的对象,查看器是基于模型绘制的对象。

摆动计时器的时间不是很精确,这对许多事情都很好,但有时你需要更准确地安排代码。在这种情况下,您可能希望使用java.util.Timer类。你这样使用它:

//imports (before class definition)
import java.util.Timer;
import java.util.TimerTask;

//inner class that does the calculations
public class CalculateTask extends TimerTask {
    public void run() {
        model.calculate();
        view.repaint();
    }
}

//put this code where you want to start doing calculations
int delay = 0;//time before running CalculateTask.run()
int repeat = 1000; //time between each subsequent rums of CalculateTask.run()
boolean isDaemon = true;//allow java to shutdown without stopping timer
Timer timer = new Timer(isDaemon);
timer.scheduleAtFixedRate(new CalculateTask(), delay, repeat); 

答案 5 :(得分:0)

wait()永远不会被释放,因为您没有在同一个对象上进行同步。您的计算方法位于不同的对象中,因此通知调用不与您的mark()方法共享同一个监视器。

等待/通知机制适用于共享监视器,也就是说,它们必须共享相同的线程锁定机制。

wait()'唤醒'的唯一方法是,如果另一个线程在同一个对象的同步块中调用notify()。

答案 6 :(得分:0)

不幸的是,wait()永远不会停止等待。 主要原因,看你是把你的notify()。 它被同一个线程调用,无法唤醒自己。

这很简单。到达wait()命令时,mark(int var)已经完成运行,因此mark(int var)中的notify()无法将其唤醒。