是否可以等待非';在Java中完成的线程数是多少?

是否可以等待非';在Java中完成的线程数是多少?,java,multithreading,concurrency,Java,Multithreading,Concurrency,我有一个物体可以做一些计算,然后对于每一个动作,我想画出发生了什么。画画的时候,我想让它等一等 基本上,我就是这么做的: synchronized public void compute() { other.mark(variable); try { wait(); } catch(InterruptedException e) { } } 在另一节课上,我有 synchronized public void mark(i

我有一个物体可以做一些计算,然后对于每一个动作,我想画出发生了什么。画画的时候,我想让它等一等

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

synchronized public void compute()
{
    other.mark(variable);
    try
    {
        wait();
    }
    catch(InterruptedException e)
    {
    }
}
在另一节课上,我有

synchronized public void mark(int var)
{
    //change some stuff
    repaint();
    notify();
}
结果是compute()会永远等待。我认为这是可行的,因为编译器没有给出错误。这两个类都没有实现可运行或扩展线程,所以这可能就是问题所在?我不确定,因为我想如果这些对象不能使用这样的方法,我会被警告

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

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

您的代码应该是:

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

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

这根本不像你想象的那样有效。从
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
保存当前的计算状态,并且
compte()
知道如何从该状态继续计算。无论您是在
compute()
还是在main循环中更改状态,都取决于您和您要解决的问题。

由于您的程序是一个GUI程序(我通过
repaint()
调用收集),因此它本质上是多线程的,即使您不知道它。(否则,它的表现将非常糟糕。)

如果不使用线程,则不能使用wait/notify或任何其他类型的同步,因为没有两个线程要同步。但您不必在GUI程序中显式地使用线程来结束使用线程

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

您有以下问题之一:

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

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

假设您实际使用的线程不止一个,使用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();
}

然后在
compute
中,当您想等待
mark()
通知时,调用
waitForSemaphore()
。因为
mark()
已经获取了信号量,所以您必须等待
mark()
释放信号量,然后compute才能通过调用
waitForSemaphore()

来获取该信号量。重新绘制方法注册了绘制组件的需要,但实际上并没有绘制该组件,但是,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();
//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); 
在上面的代码中,模型是要对其执行计算的对象,查看器是基于模型绘制的对象

swing计时器的计时不是很精确,这对很多事情来说都很好,但有时您需要更精确地安排代码。在这种情况下,您可能需要使用java.util.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();
//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); 

您的问题表明,您希望执行一些操作,在GUI状态完成或通知GUI其进度时更新GUI状态。这就是设计的目的。这两种情况在链接的javadoc上都有一些例子。

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

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

wait()将“唤醒”的唯一方法是另一个线程从同一对象的同步块中调用notify()。

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


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

这不是真的。您不必实现线程来使用wait和notify。因为这是一个GUI程序,所以它(几乎)必须是多线程的。我没有说你需要实现线程,我的意思是你需要有线程来使用wait()和notify()。(另外,您不能实现线程,您可以扩展