多线程锁定Java Swing GUI

多线程锁定Java Swing GUI,java,multithreading,swing,swingutilities,Java,Multithreading,Swing,Swingutilities,我正在为一个多用户绘制程序做一个项目,我遇到了一个GUI锁定问题 我已经完成了大部分程序,除了这个错误,它是一个相当大的程序,所以我尝试在一个较小的程序中重现错误 在这个较小的程序中,我有两个JFrames。可以通过单击并拖动鼠标来绘制这两个图形。次要JFrame是一个休眠10秒的线程,然后将您绘制的内容发送到另一个要显示的帧 但是,一旦主帧接收到来自次帧的图像,GUI将锁定,主帧将无法再绘制 我目前正在使用SwingUtilities.invokeLater()方法。在寻找答案的过程中,我找到

我正在为一个多用户绘制程序做一个项目,我遇到了一个GUI锁定问题

我已经完成了大部分程序,除了这个错误,它是一个相当大的程序,所以我尝试在一个较小的程序中重现错误

在这个较小的程序中,我有两个JFrames。可以通过单击并拖动鼠标来绘制这两个图形。次要JFrame是一个休眠10秒的线程,然后将您绘制的内容发送到另一个要显示的帧

但是,一旦主帧接收到来自次帧的图像,GUI将锁定,主帧将无法再绘制

我目前正在使用SwingUtilities.invokeLater()方法。在寻找答案的过程中,我找到了SwingWorker类,但我想先看看是否有一个简单的解决方案,然后再对我的代码进行大规模重写,以使其与SwingWorker一起工作

谢谢你的阅读。我的代码如下。还有,这是我第一次在这里发帖。我似乎在格式化代码时遇到了一些问题,所以如果出现错误,我会提前道歉。我会尽我最大的努力把它修好

package gui_thread_test;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JComponent;

public class DrawPanel extends JComponent
{
    private Image canvas;
    private int x, y, prevX, prevY;
    Graphics2D g2;

    public DrawPanel()
    {
        setDoubleBuffered(false);
        addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent e )
            {
                prevX = e.getX();
                prevY = e.getY();
            }
        });

        addMouseMotionListener(new MouseMotionAdapter()
        {
            @Override
            public void mouseDragged(MouseEvent e)
            {
                x = e.getX();
                y = e.getY();
                g2.drawLine(prevX, prevY, x, y);
                repaint();
                prevX = x;
                prevY = y;
            }
        });
    }

    @Override
    public void paintComponent(Graphics g)
    {
        if (canvas == null)
        {
            canvas = createImage(getSize().width, getSize().height);
            g2 = (Graphics2D) canvas.getGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        }
        g.drawImage(canvas, 0, 0, null);
    }

    public synchronized void updateCanvas(Image _canvas)
    {
        canvas = _canvas;
        repaint();
    }

    public Image getImage()
    {
        return canvas;
    }
}
-

-


每10秒传输一次,使用一个摆动计时器而不是线程。使用BuffereImage保存图形并传输图形

例如:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class GuiDoubleDraw extends JPanel {
   private static final int BI_WIDTH = 500;
   private static final int BI_HEIGHT = BI_WIDTH;
   public static final Color PEN_COLOR = Color.black;
   private BufferedImage backgroundImg = new BufferedImage(BI_WIDTH, BI_HEIGHT,
         BufferedImage.TYPE_INT_ARGB);

   public GuiDoubleDraw() {
      MyMouseAdapter mouseAdapter = new MyMouseAdapter();
      addMouseListener(mouseAdapter);
      addMouseMotionListener(mouseAdapter);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (backgroundImg != null) {
         g.drawImage(backgroundImg, 0, 0, this);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(BI_WIDTH, BI_HEIGHT);
   }

   public BufferedImage getBackgroundImage() {
      BufferedImage copyImg = new BufferedImage(BI_WIDTH, BI_HEIGHT,
            BufferedImage.TYPE_INT_ARGB);
      Graphics g = copyImg.getGraphics();
      g.drawImage(backgroundImg, 0, 0, null);
      g.dispose();
      return copyImg;
   }

   public void setBackgroundImage(BufferedImage bImg) {
      this.backgroundImg = bImg;
      repaint();
   }

   private class MyMouseAdapter extends MouseAdapter {
      Point previousPt = null;

      @Override
      public void mousePressed(MouseEvent mEvt) {
         previousPt = mEvt.getPoint();
      }

      @Override
      public void mouseDragged(MouseEvent mEvt) {
         drawPt(mEvt);
      }

      @Override
      public void mouseReleased(MouseEvent mEvt) {
         drawPt(mEvt);
      }

      private void drawPt(MouseEvent mEvt) {
         Graphics g = backgroundImg.getGraphics();
         Point nextPt = mEvt.getPoint();
         g.setColor(PEN_COLOR);
         g.drawLine(previousPt.x, previousPt.y, nextPt.x, nextPt.y);
         g.dispose();
         previousPt = nextPt;
         repaint();
      }

   }

   private static void createAndShowGui() {
      final GuiDoubleDraw guiDoubleDraw1 = new GuiDoubleDraw();
      final GuiDoubleDraw guiDoubleDraw2 = new GuiDoubleDraw();

      JFrame frame = new JFrame("Draw 1");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(guiDoubleDraw1);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);

      JDialog dialog = new JDialog(frame, "Draw 2", ModalityType.MODELESS);
      dialog.getContentPane().add(guiDoubleDraw2);
      dialog.pack();
      dialog.setLocationRelativeTo(null);
      dialog.setVisible(true);

      int timerDelay = 10 * 1000;
      new Timer(timerDelay, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            guiDoubleDraw1.setBackgroundImage(guiDoubleDraw2
                  .getBackgroundImage());
         }
      }).start();
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

您将希望在代码中实现SwingWorker,这将允许您在事件调度线程上和下运行代码。即使您应该在这个场景中找到解决方法,SwingWorker也是使用线程更新UI的正确方法。你会发现,使用各种线程并尝试在没有SwingWorker的情况下更新UI通常会“把你自己弄到一个角落”。我会重构并将其作为一种积极的学习体验。

你的应用程序没有锁定,你的
线程正在退出

这里没有任何东西可以通过终止
线程来阻止
运行
方法的完成,这意味着不再有任何更新

public void run() {
    //Sleep thread for 10 seconds to give time to draw on the image
    try {
        Thread.sleep(10000);
    } catch (InterruptedException ex) {
        Logger.getLogger(ThreadTest.class.getName()).log(Level.SEVERE, null, ex);
    }

    SwingUtilities.invokeLater(
            new Runnable() {
                @Override
                public void run() {
                    System.out.println("Post...");
                    //Posts the message to the server chat window
                    panel.updateCanvas(threadPanel.getImage());
                }
            });
}

HoverCraftFullOfels是正确的,请改用。它将以固定的时间间隔继续运行。

虽然“技术上”没有错,
javax.swing.Timer
将更适合手头的问题-定期、计划的更新…IMHO(更不用说更容易了)我计划重构它,以获得使用SwingWorker的一些经验,现在我已经找到了它,但在我有时间了解SwingWorker之前,我必须先展示一些工作代码,但在我尝试向完整程序添加更多功能之前,SwingWorker的重构肯定是我的任务清单上的一部分。事实上,这是一个答案,事实上说明了原始海报代码的问题所在。1+线程正是我用来重现错误的。问题是您可以在主框架中绘制。次帧向主帧发送图像,主帧显示图像。现在,主框架不能被拉入。传输实际上是通过我的流套接字连接来的。我只是以线程睡眠为例来重现我的锁定错误。但是,我检查了你的代码,它似乎解决了这个问题。我现在将更深入地研究它。我想我应该能解决我的问题。也许不使用BuffereImage是我错的地方。谢谢
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class GuiDoubleDraw extends JPanel {
   private static final int BI_WIDTH = 500;
   private static final int BI_HEIGHT = BI_WIDTH;
   public static final Color PEN_COLOR = Color.black;
   private BufferedImage backgroundImg = new BufferedImage(BI_WIDTH, BI_HEIGHT,
         BufferedImage.TYPE_INT_ARGB);

   public GuiDoubleDraw() {
      MyMouseAdapter mouseAdapter = new MyMouseAdapter();
      addMouseListener(mouseAdapter);
      addMouseMotionListener(mouseAdapter);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (backgroundImg != null) {
         g.drawImage(backgroundImg, 0, 0, this);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(BI_WIDTH, BI_HEIGHT);
   }

   public BufferedImage getBackgroundImage() {
      BufferedImage copyImg = new BufferedImage(BI_WIDTH, BI_HEIGHT,
            BufferedImage.TYPE_INT_ARGB);
      Graphics g = copyImg.getGraphics();
      g.drawImage(backgroundImg, 0, 0, null);
      g.dispose();
      return copyImg;
   }

   public void setBackgroundImage(BufferedImage bImg) {
      this.backgroundImg = bImg;
      repaint();
   }

   private class MyMouseAdapter extends MouseAdapter {
      Point previousPt = null;

      @Override
      public void mousePressed(MouseEvent mEvt) {
         previousPt = mEvt.getPoint();
      }

      @Override
      public void mouseDragged(MouseEvent mEvt) {
         drawPt(mEvt);
      }

      @Override
      public void mouseReleased(MouseEvent mEvt) {
         drawPt(mEvt);
      }

      private void drawPt(MouseEvent mEvt) {
         Graphics g = backgroundImg.getGraphics();
         Point nextPt = mEvt.getPoint();
         g.setColor(PEN_COLOR);
         g.drawLine(previousPt.x, previousPt.y, nextPt.x, nextPt.y);
         g.dispose();
         previousPt = nextPt;
         repaint();
      }

   }

   private static void createAndShowGui() {
      final GuiDoubleDraw guiDoubleDraw1 = new GuiDoubleDraw();
      final GuiDoubleDraw guiDoubleDraw2 = new GuiDoubleDraw();

      JFrame frame = new JFrame("Draw 1");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(guiDoubleDraw1);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);

      JDialog dialog = new JDialog(frame, "Draw 2", ModalityType.MODELESS);
      dialog.getContentPane().add(guiDoubleDraw2);
      dialog.pack();
      dialog.setLocationRelativeTo(null);
      dialog.setVisible(true);

      int timerDelay = 10 * 1000;
      new Timer(timerDelay, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            guiDoubleDraw1.setBackgroundImage(guiDoubleDraw2
                  .getBackgroundImage());
         }
      }).start();
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}
public void run() {
    //Sleep thread for 10 seconds to give time to draw on the image
    try {
        Thread.sleep(10000);
    } catch (InterruptedException ex) {
        Logger.getLogger(ThreadTest.class.getName()).log(Level.SEVERE, null, ex);
    }

    SwingUtilities.invokeLater(
            new Runnable() {
                @Override
                public void run() {
                    System.out.println("Post...");
                    //Posts the message to the server chat window
                    panel.updateCanvas(threadPanel.getImage());
                }
            });
}