【问题标题】:Passing information between EDT and other threads Java with a Handler使用 Handler 在 EDT 和其他 Java 线程之间传递信息
【发布时间】:2017-08-20 10:31:53
【问题描述】:

如果按下按钮,我有一个 GUI(在其 EDT 线程上)和另一个名为“Recorder”的线程,由 GUI 的 eventListener 创建:

if(actionEvent.getSource().equals(ui.record)) {
    if(recorderThread == null) {
        recorder = new Recorder();
        recorderThread = new Thread(recorder);
        recorderThread.start();
    }
}

在同一个事件监听器中,我还实现了一个mouseListener。

public void mouseReleased(MouseEvent mEvent) {
    int x, y;

    x = mEvent.getXOnScreen();
    y = mEvent.getYOnScreen();
}

单击鼠标时,我想将这些 X 和 Y 变量传递给记录器线程中的记录器对象。我想我可以用 volatile 变量提出一个解决方案,但我在某处读到处理程序可用于在两个线程之间传递信息或调用方法,并且有兴趣了解它。我发现this previous post 面临与我类似的问题。

然而,帖子的解决方案让我很困惑。我认为这个人将线程对象传递给处理程序,这样任何线程都可以调用该处理程序内的所有对象?例如:

handler(someObj);

然后在另一个线程中

handler.getSomeObj().methodInObj();

但我不完全确定处理程序是否是这样工作的。此外,他们似乎也在处理 Swing 的后台线程,而不是用户创建的单独线程(如果这是相同的概念,请提前道歉)。

最后,解决方案似乎调用了 Java 库中内置的 Handler 类,而我想编写自己的处理程序类,以便更好地了解线程如何通信(因为我是一个真正的新手 youtube 教程序员)。如果有人可以帮助我,将不胜感激。提前致谢!

【问题讨论】:

  • 当您的 GUI 运行时,Recorder 到底在后台做什么?它正在进行什么活动?
  • 此外,您永远不会在 线程 之间发送信息,而是在 对象 之间发送信息,这是一个微妙但非常重要的区别。
  • 感谢您的澄清!记录器只是创建一个名为“Point”的对象列表,用于存储鼠标点击的 X 和 Y 位置,供自动点击器稍后使用。

标签: java multithreading swing handler


【解决方案1】:

一方面区分线程的概念,另一方面区分类的概念(包括实例及其成员)。

线程可以通过多种方式进行通信(意味着在其他线程可以写入或读取的位置读取或写入变量。在您的示例中,我会让您的 Recorder 类公开一个公共方法 addCoordinates()。Recorder 有一个存储添加坐标的私有列表成员。真正的问题是对该列表的同步访问:您必须确保没有一个线程读取列表,而另一个线程同时添加新记录。最简单的解决方案是有一个同步列表:

private List<Coordinates> myCoordinates = Collections.synchronizedList( new ArrayList<>());

public void addCoordinates( Coordinates coordinates)
{ 
    // this runs in the context of your GUI thread
    myCoordinates.add(coordinates);
    synchronized(this)
    {
        this.notify();    // wakes up the recorder thread
    }

}

public void run()
{
    // this runs in the context of the Recorder thread
    while ( true)
    {
        synchronized(this)
        {
            this.wait();   // waits until the 'this' is notified
        }

        for( Coordinates c : myCoordinates)
        {
            // do something
        }
    }
}

【讨论】:

    【解决方案2】:

    记录器只是创建一个名为“Point”的对象列表,用于存储鼠标点击的 X 和 Y 位置,供自动点击器稍后使用

    那么 Recorder 不应该在它自己的线程中运行,因为没有必要这样做,而您只需直接从 GUI 写入记录器在 EDT 线程上。否则,您会使事情变得过于复杂。例外情况是,如果 Recorder 实际上正在执行更多操作,例如当前正在运行外部进程。

    例如:

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.GridLayout;
    import java.awt.Point;
    import java.awt.event.ActionEvent;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class TestRecorder extends JPanel {
        private static final int GAP = 3;
        private MyRecorder myRecorder = new MyRecorder();
        private boolean recording = false;
    
        public TestRecorder() {
            JPanel btnPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
            btnPanel.add(new JButton(new StartAction("Start")));
            btnPanel.add(new JButton(new StopAction("Stop")));
            btnPanel.add(new JButton(new ShowAction("Show")));
    
            addMouseListener(new MyMouse());
            setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
            setPreferredSize(new Dimension(500, 400));
            setLayout(new BorderLayout());
            add(btnPanel, BorderLayout.PAGE_END);
        }
    
        private class MyMouse extends MouseAdapter {
            @Override
            public void mousePressed(MouseEvent e) {
                if (recording) {
                    myRecorder.addPoint(e.getPoint());
                }
            }
        }
    
        private class StartAction extends AbstractAction {
            public StartAction(String name) {
                super(name);
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                recording = true;
            }
        }
    
        private class StopAction extends AbstractAction {
            public StopAction(String name) {
                super(name);
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                recording = false;
            }
        }
    
        private class ShowAction extends AbstractAction {
            public ShowAction(String name) {
                super(name);
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Points:");
                for (Point point : myRecorder.getPoints()) {
                    System.out.println(point);
                }
                System.out.println();
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    
        private static void createAndShowGui() {
            TestRecorder mainPanel = new TestRecorder();
            JFrame frame = new JFrame("TestRecorder");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.add(mainPanel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    }
    

    public class MyRecorder {
        private List<Point> points = new ArrayList<>();
    
        public List<Point> getPoints() {
            return points;
        }
    
        public void addPoint(Point p) {
            points.add(p);
        }
    }
    

    【讨论】:

    • 感谢您的回复!是的,我有点意识到,在没有被剥夺睡眠和试图理解线程时激怒了-.-问题是我还想将相同的进程复制到使用机器人类的自动点击器线程上,这肯定需要它自己的线。我只是选择使用记录器作为示例,因为它更简单并且不会混淆问题。再次感谢您的帮助!
    • 也许我应该将它与机器人对象合并?这样,记录器创建的任何列表都可以由机器人直接访问,而无需其他复杂的列表和变量传递。 *旁注:注意到你是我发布的几乎每个问题都非常有帮助的家伙,所以只想给一个特别的 SO 并谢谢你(:
    • @Crumble:你犯了一个大的新手错误,将对象与线程混淆。您的自动点击器是一个对象,它可能在一个线程上运行,并且它可能具有在不同线程上运行的方法。当您将信息传递到 autoclicker 时,您不是将其传递到线程中,而是传递到对象中,并且通常如果它来自 GUI,它将进入 Swing 事件线程上的 autoclicker 对象。跨度>
    猜你喜欢
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多