Java EDT与主线程之间的通信

Java EDT与主线程之间的通信,java,multithreading,swing,event-dispatch-thread,Java,Multithreading,Swing,Event Dispatch Thread,我最近一直在问很多关于一个项目的问题。这是我所处的场景,任何帮助或指向正确方向的点都会有很大帮助 这是一个由一个服务器和多个客户端构建的网络程序。每个客户端都有一个GUI,它必须根据服务器发送的命令进行操作。每个客户端都封装在一个名为Player的类中。这个Player有一个GUI(扩展JFrame)和一个main方法,而服务器只有一个main方法(没有GUI)。首先,该类是在主线程中创建的,如下所示: EventQueue.invokeLater(new Runnable() { pu

我最近一直在问很多关于一个项目的问题。这是我所处的场景,任何帮助或指向正确方向的点都会有很大帮助

这是一个由一个服务器和多个客户端构建的网络程序。每个客户端都有一个GUI,它必须根据服务器发送的命令进行操作。每个客户端都封装在一个名为
Player
的类中。这个
Player
有一个GUI(扩展
JFrame
)和一个main方法,而服务器只有一个main方法(没有GUI)。首先,该类是在主线程中创建的,如下所示:

EventQueue.invokeLater(new Runnable()
{
    public void run()
    {
        new Player().setVisible(true);
    }
 });
class Player
{
    public GUI gui;

    ...

    // And then start this gui inside of the EDT.
    EventQueue.invokeLater(new Runnable()
    {
         public void run()
         {
              this.gui = new GUI().setVisible(true);
         }
    }
这很好,直到我意识到整个
Player
类现在都在EDT中执行。因此,当我等待来自服务器的命令时,整个GUI将锁定,直到发送该命令并执行正确的操作为止。正如您可以想象的,这是一个可怕的设计,并被证明是一个真正的痛苦的编码环境,当您每次想要检查的东西,您必须找到一些疯狂的工作,使GUI仍然保持完整

显然,我必须在单独的线程中检查来自服务器的命令,并在EDT中运行GUI组件。我的第二个实现有两个类——一个用于GUI,另一个用于
播放器。这个想法是
Player
有一个保存GUI的变量,这样我就可以从
Player
类访问GUI,如下所示:

EventQueue.invokeLater(new Runnable()
{
    public void run()
    {
        new Player().setVisible(true);
    }
 });
class Player
{
    public GUI gui;

    ...

    // And then start this gui inside of the EDT.
    EventQueue.invokeLater(new Runnable()
    {
         public void run()
         {
              this.gui = new GUI().setVisible(true);
         }
    }
这也不起作用,因为新的
Runnable
对象中的
This
指的是
Runnable
对象,而不是
Player


如何在一个线程中的
Player
类和EDT线程中相应的GUI类之间进行通信?

与其使用匿名内部类,不如只声明一个实现可运行的类,并使用一个以GUI实例为参数的构造函数

此外,如果您的GUI类不是线程安全的,请考虑使用消息队列在EDT和主线程之间进行通信。


  • 要处理
    指针的问题,您应该写:

    class Player
    {
        public GUI gui;
    
        ...
    
        // And then start this gui inside of the EDT.
        EventQueue.invokeLater(new Runnable()
        {
             public void run()
             {
                  Playser.this.gui = new GUI().setVisible(true);
             }
        }
    }
    
    你可以试试这个:

    职业选手 { 公共GUI

    ...
    
    // And then start this gui inside of the EDT.
    EventQueue.invokeLater(new Runnable()
    {
         public void run()
         {
              Player.this.gui = new GUI().setVisible(true);
         }
    }
    

    Boris Pavlović获得了正确的语法(实际上您可以删除
    this.
    ),但代码仍然没有意义。
    gui
    字段是在
    Runnable
    事件排队后初始化的,因此播放器线程使用它是不安全的

    您可以在EDT上构造
    Player
    (但在EDT下执行网络操作)。或者将GUI注册为
    Player的侦听器(观察者)
    invokeAndWait
    可以工作,但危险的是,它经常会导致偶尔出现难以调试的死锁。

    “直到我意识到整个玩家类现在都在EDT中执行”

    构造函数发生在EDT上,但在此类上调用的方法可能不存在

    您应该按照最初的意图构建播放器GUI

     EventQueue.invokeLater(new Runnable() 
     {
        public void run()
        {
            new Player().setVisible(true);
        }
     });
    
    但是玩家可以在构造器中启动一个单独的线程(就我个人而言,我会在玩家之间共享一个连接)

    当然,在修改可见组件时,来自服务器的回调方法应该使用invokeLater()