Java SwingUtilities.invokeLater未按预期工作

Java SwingUtilities.invokeLater未按预期工作,java,swing,swingutilities,invokelater,Java,Swing,Swingutilities,Invokelater,我正在开发一个用Java编写的并发画布,这会让用户觉得他们是在并行地在画布上绘图 为了实现用户感知的并行性,我让他们创建这些可运行的对象,然后使用SwingUtilities.invokeLater()将它们放在EventQueue上 为了测试它,我使用两个线程模拟了用户,并在每次调用invokeLater()之间增加了一点延迟(大约50毫秒),以查看绘图是否真的是并行进行的 问题是,尽管它在invokeLater()调用之间增加了延迟,但去掉延迟有时会导致正确绘制图形,有时会部分绘制并消失,而

我正在开发一个用Java编写的并发画布,这会让用户觉得他们是在并行地在画布上绘图

为了实现用户感知的并行性,我让他们创建这些可运行的对象,然后使用SwingUtilities.invokeLater()将它们放在EventQueue上

为了测试它,我使用两个线程模拟了用户,并在每次调用invokeLater()之间增加了一点延迟(大约50毫秒),以查看绘图是否真的是并行进行的

问题是,尽管它在invokeLater()调用之间增加了延迟,但去掉延迟有时会导致正确绘制图形,有时会部分绘制并消失,而在其他时间则不会绘制

我对可能出现的问题感到困惑,所以如果有人有任何想法,请让我知道

以下是注释掉延迟的代码:

public void run(){
    //add tasks on to the event queue of the EDT 
    for(int i = 0; i<numLines; i++){
        DrawLineTask task = new DrawLineTask(g, x1, y1+i, x2, y2+i, lineColor);
        SwingUtilities.invokeLater(task);
//          try {
//    Thread.sleep(new Double(Math.random()*50).longValue());//random sleeping times to             make it appear more realistic
//          } catch (InterruptedException e) {
//              e.printStackTrace();
//          }
    }

顺便说一句,你不应该保留一个图形对象的引用,并在你想画的时候在上面画。相反,您应该等待Swing调用
paintComponent()
方法,并使用此方法绘制图形

因此,您的任务应该只更改组件的状态,并请求异步或同步重新绘制(使用
repaint()
paintright()
)。Swing随后将使用图形对象调用
paintComponent()
方法,您可以根据组件的状态使用该对象绘制适当的线条


有关更多详细信息和说明,请参阅。

好的,您不应该保留图形对象的引用,并在需要时在其上绘制。相反,您应该等待Swing调用
paintComponent()
方法,并使用此方法绘制图形

因此,您的任务应该只更改组件的状态,并请求异步或同步重新绘制(使用
repaint()
paintright()
)。Swing随后将使用图形对象调用
paintComponent()
方法,您可以根据组件的状态使用该对象绘制适当的线条


有关更多详细信息和解释,请参阅。

这里只是猜测,但我猜测是线程。睡眠使运行时更容易切换到EDT进行绘图,这意味着绘图将立即进行。同意第一条评论,但我怀疑还有另一个问题。你能发布DrawLineTask的代码吗?谢谢你们的回复。正如您在上面看到的,绘图代码非常简单,即使它在某种程度上是计算密集型的,EDT可能会稍微慢一点,但它最终还是会绘制线条。然而,您所说的似乎是正确的,因为绘图可以完美地工作,甚至有1ms的睡眠延迟。但是,当我将其设置为0毫秒时,它又开始出现问题。这里只是猜测,但我猜测的是
线程。睡眠
使运行时更容易切换到EDT进行绘图,这意味着它将立即发生。同意第一条评论,但我怀疑还有另一个问题。你能发布DrawLineTask的代码吗?谢谢你们的回复。正如您在上面看到的,绘图代码非常简单,即使它在某种程度上是计算密集型的,EDT可能会稍微慢一点,但它最终还是会绘制线条。然而,您所说的似乎是正确的,因为绘图可以完美地工作,甚至有1ms的睡眠延迟。但只要我让它0毫秒,它就会再次出现问题。我理解这一点。但是,我正在尝试将绘图任务(命令)与其要绘制的组件分离,以便外部的任何人都可以生成绘图任务并将其绘制到组件上。尽管如此,由于生成任务的实体将引用它们要绘制的组件,因此我将继续尝试您的建议,并回复您。谢谢所以我做了更多的测试,结果表明,有时会出现图形,有时不会,这是因为有时会在绘制任务后调用repaint(),从而清除组件。我知道确保以前的图纸不会丢失的一种方法是将我的图纸代码放在paintComponent()中,但我希望用户继续绘制,这样最终不会对系统一次又一次地重新绘制图纸造成一定的负担?我如何确保以前的图纸保留在paintComponent()中,因为它们都被包装成任务?我需要在paintComponent()方法中维护某种列表吗?我将在paintComponent()方法中接收到的所有绘图任务存储在一个同步列表中,并在该列表上迭代以调用任务上的draw()方法。那太好了!我明白。但是,我正在尝试将绘图任务(命令)与其要绘制的组件分离,以便外部的任何人都可以生成绘图任务并将其绘制到组件上。尽管如此,由于生成任务的实体将引用它们要绘制的组件,因此我将继续尝试您的建议,并回复您。谢谢所以我做了更多的测试,结果表明,有时会出现图形,有时不会,这是因为有时会在绘制任务后调用repaint(),从而清除组件。我知道确保以前的图纸不会丢失的一种方法是将我的图纸代码放在paintComponent()中,但我希望用户继续绘制,这样最终不会对系统一次又一次地重新绘制图纸造成一定的负担?我如何确保以前的图纸保留在paintComponent()中,
public class DrawLineTask implements Runnable {
Graphics g;
int x1 = 0;
int y1 = 0;
int x2 = 0;
int y2 = 0;
Color color = Color.BLACK;

public DrawLineTask(Graphics g, int x1, int y1, int x2, int y2){
    this.g = g;
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
}

public DrawLineTask(Graphics g, int x1, int y1, int x2, int y2, Color color){
    this.g = g;
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    this.color = color;
}

@Override
public void run() {
    g.setColor(color);
    g.drawLine(x1, y1, x2, y2);
}

}