Java GUI和持久后端之间的通信(在单独的线程中运行)

Java GUI和持久后端之间的通信(在单独的线程中运行),java,multithreading,synchronization,producer-consumer,Java,Multithreading,Synchronization,Producer Consumer,你好,我知道有很多关于它的问题和一些相关的答案。类似于从后端使用将响应返回到GUI JTextArea,以及使用将消息传递到后端。这个我可以用 但我想跳过实现队列的消息解析器。我想知道是否有可能直接从另一个线程调用方法。好的部分答案是,但它只能启动单个任务线程。我要寻找的是一个持久对象,它接受从另一个线程调用的更多方法并执行序列化 更具体地说: 第一个线程是GUI,具有多个按钮,如“打开设备”、“将注册表A设置为用户输入”、“将注册表B设置为用户输入”、“启用功能X”、“flash FW” 第二

你好,我知道有很多关于它的问题和一些相关的答案。类似于从后端使用将响应返回到GUI JTextArea,以及使用将消息传递到后端。这个我可以用

但我想跳过实现队列的消息解析器。我想知道是否有可能直接从另一个线程调用方法。好的部分答案是,但它只能启动单个任务线程。我要寻找的是一个持久对象,它接受从另一个线程调用的更多方法并执行序列化

更具体地说:
第一个线程是GUI,具有多个按钮,如“打开设备”、“将注册表A设置为用户输入”、“将注册表B设置为用户输入”、“启用功能X”、“flash FW”

第二个是一个工作线程-它已经由多个类组成。并且具有需要从GUI调用的方法

我需要以下属性
-工作线程只有1个,并且在所有GUI调用中都是持久的
-所有GUI调用都应序列化(只有在第一个调用完全处理并返回后,才会启动另一个调用)
-工作线程应该能够将一些“日志消息”发送到GUI中(例如%的闪现FW)(这可能很容易通过以下方式实现)


有没有比实现队列解析器更好的方法来调用方法?如果有,你能提供一些好例子的链接吗?或者队列是执行此任务的正确方法?如果队列是正确的方法,如何最好地编码不同的参数?例如,“闪存固件”按钮需要传递“文件”,“将注册表A设置为值XY”按钮需要传递字节…

您可以使用Executors.newSingleThreadExecutor()创建一个执行器来运行任务。一旦有了Executor实例,就可以向它发送可运行的对象。任务将排队,并且每个任务将在下一个任务开始之前运行到完成,所有任务都使用相同的工作线程。 例如,在UI线程中,您可以向执行者发送任务,如下所示:

ExecutorService executor = Executors.newSingleThreadExecutor();
...
JButton b1 = new JButton("doTask1");
b1.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) { 
        executor.execute(yourRunnable1);
    });
});

JButton b2 = new JButton("doTask2");
b2.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) { 
        executor.execute(yourRunnable2);
    });
});
基于用户“mevqz”的回答,我做了一个示例代码。这是完全有效的,似乎是对我的问题的好答案。我将提供这个示例代码作为详细的答案,因为我仍然需要付出很大的努力才能将其组合起来。作为一个新手,我想问一下我是否正确地得到了mevqz提示,我的代码是否真的是线程安全的

这里基本上只是一个原始后端,在这里我实现了调用方法log()的可能性,该方法将以线程安全的方式写回GUI JTextArea

import javax.swing.*;

public class Backend {
    private int handle=0;
    private int regA=0;
    Main guiLogger;
    Backend(Main guiLog){ // interface having log() would be enough
       guiLogger = guiLog;
    }
    public void log(final String txt){
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
               guiLogger.log(txt);
            }
         });
    }

    public void open(){
        log("openning\n");
        // some code that work longer time...
        try{
            Thread.sleep(1000);
        }catch(Exception e){}
        handle++;
        log("opened: "+handle+"\n");
    }
    public void setRegA(int val){
        log("using handle:"+handle+" set reg A val: "+val+"\n");
        regA = val;
    }
}
下面是保存executorService和后端引用的包装器。在这里,它似乎不是很好,因为后端实际上存储在错误的线程中,并且总是传递到Runnable.run()。是否有更好的方法将后端引用直接隐藏到ExecutorService中

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BackendRunWrapper{
    private Backend backend; // never call it from this thread
    private ExecutorService executor;

    public BackendRunWrapper(Main logger){
        backend = new Backend(logger);
        executor = Executors.newSingleThreadExecutor();
    }
    public void executorShutdown(){
        executor.shutdown();
    }
    public void open(){
        executor.execute(new Runnable(){
            public void run(){
                BackendRunWrapper.this.backend.open();
            }
        });
    }
    public void setRegA(final int val){
        executor.execute(new Runnable(){
            public void run(){
                BackendRunWrapper.this.backend.setRegA(val);
            }
        });
    }
}
这里是一个主要的gui,有两个按钮“Open”和“SetRegA”以及用于日志记录的JTextArea。这仅从BackendRunWrapper调用函数。这里唯一的问题是executorShutdown()调用是否正确

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Main{
    BackendRunWrapper backendWrapper;
    StringBuilder sb;
    JTextArea jta;
    public void log(String txt){
        sb.append(txt);
        jta.setText(sb.toString());
    }
    public Main(){
        backendWrapper = new BackendRunWrapper(this);
        sb = new StringBuilder();

        JFrame frame = new JFrame("Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container pane = frame.getContentPane();
        pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));

        JButton b1 = new JButton("open");
        b1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                backendWrapper.open();
            }});
        pane.add(b1);

        JButton b2 = new JButton("setRegA");
        b2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                backendWrapper.setRegA(42);
            }});
        pane.add(b2);

        jta = new JTextArea(20, 80);
        pane.add(jta);

        frame.pack();
        frame.setVisible(true);

        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            public void run() {
                Main.this.backendWrapper.executorShutdown();
            }}));
    }

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

别忘了指出Swing不是线程安全的,因此op可能需要做出响应的任何更新都需要在事件调度线程的上下文中执行,并且根据ops的要求,SwingWorker可能是一个更好的解决方案谢谢你mevqz你能看看我的例子吗,如果它是你的意思和线程安全的?