简体   繁体   English

是否可以等待非Threads中的方法在Java中完成?

[英]Is it possible to wait for methods that aren't in Threads to finish in Java?

I have an object which does some computation, then for each iteraction I want to draw what happens. 我有一个进行一些计算的对象,然后对于每个迭代,我想画出发生的情况。 While the drawing is happening, I want it to wait. 在绘制过程中,我希望它等待。

This is what I did, basically: 这是我所做的,基本上是:

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

in the OtherClass, I have 在OtherClass中,我有

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

What happens is that compute() waits forever. 发生的事情是compute()永远等待。 I thought this was going to work, since no erros were given by the compiler. 我认为这将行得通,因为编译器未给出任何错误。 Neither class implements Runnable or extends Thread, so maybe that's the problem? 这两个类都没有实现Runnable或扩展Thread,所以也许是问题所在吗? I'm not sure since I figure I'd be warned if these objects couldn't use such methods. 我不确定,因为我认为如果这些对象不能使用此类方法,我会被警告。

I'm thinking it might be an error regarding the logic of the program itself, but in a nutshell that's what I have. 我想这可能是关于程序本身的逻辑的错误,但总的来说,这就是我所拥有的。

Your question suggests that you want to perform some operation that updates the GUI state either when it is finished or notifies the GUI of its progress. 您的问题建议您要执行一些操作,以在完成时更新GUI状态或将其进度通知给GUI。 This is what SwingWorker was designed for. 这就是SwingWorker的目的。 There are some examples on the linked javadoc for both cases. 对于这两种情况,链接的javadoc上都有一些示例。

This simply does not work as you think it does. 这根本无法像您想象的那样起作用。 From Javadoc of wait() method (emphasis mine): 从Javadoc的wait()方法开始(重点是我):

Causes current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. 使当前线程等待,直到另一个线程为此对象调用notify()方法或notifyAll()方法。

There is obviously no other thread in your program to wake up the sleeping compute() method. 程序中显然没有其他线程可以唤醒休眠的compute()方法。

To solve your particular problem, you either have to have two threads, or, alternatively, implement compute() method as resumable, something like this in pseudo Java: 为了解决您的特定问题,您要么必须有两个线程,要么要以可恢复的方式实现compute()方法,这在伪Java中是这样的:

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

Here ComputeStatus hold the current state of computation, and comupte() knows how to continue the calculation from that state. 在这里, ComputeStatus保持计算的当前状态,而comupte()知道如何从该状态继续计算。 Whether you change the status in compute() or in main loop, is up to you and the problem you're solving. 无论是在compute()还是在主循环中更改状态,都取决于您和您要解决的问题。

Object.wait() and Object.notify() are only for use by threads. Object.wait()和Object.notify()仅供线程使用。 In the code you show, the method mark(int var) will not return until it is finished, there is no need to wait. 在显示的代码中,mark(int var)方法直到完成才返回,无需等待。 Also, synchronized methods are only needed in a multi-threaded program. 此外,仅在多线程程序中才需要同步方法。

Your code should be: 您的代码应为:

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

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

Since your program is a GUI program (I'm gathering by the repaint() call), it is inherently multi-threaded, even if you don't know it. 由于您的程序是一个GUI程序(我通过repaint()调用收集),因此它固有地是多线程的,即使您不知道它也是。 (If not, it will behave very badly.) (如果没有,它将表现得很差。)

If you are not using Threads, then you cannot use wait/notify or any other kind of synchronization, since there aren't two threads to synchronize. 如果您不使用线程,则不能使用等待/通知或任何其他类型的同步,因为没有两个线程可以同步。 But you don't have to explicitly use Threads, necessarily, in a GUI program to end up using Threads. 但是,不必一定要在GUI程序中显式地使用线程来最终使用线程。

Note that the Java compiler won't warn you if you use methods relying on Threads but don't actually use Threads in any way. 请注意,如果您使用依赖线程的方法,但实际上不以任何方式使用线程,则Java编译器不会警告您。

You have one of the following problems: 您遇到以下问题之一:

1) You are using Threads without knowing about it and you are using two different monitor objects. 1)您正在使用线程而不知道它,并且您正在使用两个不同的监视对象。 Thus, when you call notify() , it is notifying the monitor for that object, but not for the first object where you are calling wait() . 因此,当您调用notify() ,它会通知该对象的监视器,而不是您正在调用wait()的第一个对象的监视器。 There are many possible solutions. 有许多可能的解决方案。 One of the easiest is to use the JDK 5 concurrency utilities to do this, which are MUCH nicer than the built-in base monitor wait/notify methods. 最简单的方法之一是使用JDK 5并发实用程序执行此操作,该实用程序比内置的基本监视器等待/通知方法要好得多。 Or, 要么,

2) You are running in a single Thread and the wait/notify does not good. 2)您正在单个线程中运行,并且等待/通知不好。 It just doesn't make sense in a single-Threaded program to wait for another Thread to notify -- there's no other thread that can do so. 在单线程程序中等待另一个线程通知只是没有意义-没有其他线程可以这样做。

Assuming that you are actually using more than one Thread, a good way to solve this with Java 5 and later is with a semaphore , perhaps by, in the class containing mark() and simplifying a bit: 假设您实际上使用了多个线程,使用Java 5和更高版本解决此问题的一个好方法是使用信号灯 ,也许通过在包含mark()的类中进行简化:

private final Semaphore sem = new Semaphore(1);

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

waitForSemaphore() {
  sem.acquire();
}

and then in compute , call waitForSemaphore() when you want to wait to be notified by mark() . 然后在compute ,当您想等待被mark()通知时,调用waitForSemaphore() mark() Because mark() already acquired the semaphore, you'll have to wait for mark() to release the semaphore before compute will be able to get it by calling waitForSemaphore() . 因为mark()已经获取了信号量,所以您必须等待mark()释放信号量,然后计算才能通过调用waitForSemaphore()来获取它。

The repaint method registers the need to paint the component but it doesn't actually paint it, however Java will repaint the object the next chance it gets. repaint方法记录了需要绘制组件的需要,但是实际上并没有绘制组件,但是Java将在下次获得对象时重新绘制对象。 If you are trying to make something like an animation then there is no purpose to wait for repaint to complete. 如果您要制作动画之类的东西,那么等待重绘就没有目的了。 Instead I recommend you use a timer. 相反,我建议您使用计时器。 Now you have 2 options for timers. 现在,您有2个计时器选项。 If you are updating something where the timing doesn't have to be exact then often javax.swing.Timer is what you are looking for. 如果您要更新不需要精确计时的内容,那么通常需要使用javax.swing.Timer。 You use it like this: 您可以这样使用它:

//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();

In the above code the model is the object you want to perform the calculations on and the viewer is the object that paints based on the model. 在上面的代码中,模型是您要对其执行计算的对象,查看器是根据模型进行绘制的对象。

The swing timer is not very exact in its timing which is fine for lots of things but sometimes you need code to be scheduled more exactly. Swing计时器的计时不是很精确,这在很多情况下都很好,但是有时您需要更精确地计划代码。 In that case you may want to use the java.util.Timer class. 在这种情况下,您可能要使用java.util.Timer类。 You use it like this: 您可以这样使用它:

//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); 

The wait() never gets released because you are not synchronizing on the same object. 由于您没有在同一个对象上进行同步,因此永远不会释放wait()。 Your compute method is in a different object and therefore the notify call is not sharing the same monitor as your mark() method. 您的计算方法位于另一个对象中,因此,通知调用与mark()方法共享的监视器不同。

The wait/notify mechanism are for shared monitors, that is, they have to be sharing the same thread locking mechanism. 等待/通知机制用于共享监视器,也就是说,它们必须共享相同的线程锁定机制。

The only way the wait() will 'wake up' is if another thread calls notify() from within a synchronization block on the same object. wait()将“唤醒”的唯一方法是,如果另一个线程从同一对象上的同步块内调用notify()。

Unfortunately wait() will never stop waiting. 不幸的是,wait()永远不会停止等待。 Main reason, look were you put your notify(). 主要原因是,看您是否放置了notify()。 It is being called by the same thread, and can't wake itself up. 它是由同一线程调用的,无法将其自身唤醒。

It's quite simple. 这很简单。 mark(int var) will already have finished running by the time you get to the wait() command, so there is no way that the notify() in mark(int var) can wake it. mark(int var)在您到达wait()命令时已经完成运行,因此mark(int var)中的notify()无法唤醒它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM