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

时间:2015-03-09 18:26:32

标签: java multithreading synchronization producer-consumer

您好我知道有很多问题和相关答案。就像从后端使用SwingUtilities.invokeLater向GUI JTextArea返回响应以及使用blocking queue将消息传递到后端一样。这个我可以合作。

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

更具体地说:
第一个线程是具有多个按钮的GUI,例如"打开设备","将reg A设置为用户输入","将reg B设置为用户输入","启用功能X"," flash FW" ...

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

我需要以下属性
- 工作线程只有1并且通过所有GUI调用持久化 - 所有GUI调用都应序列化(只有在第一次调用完全处理并返回后才开始另一个调用) - 工作线程应能发送一些"日志消息"进入GUI(例如闪现FW的百分比)(这可以通过SwingUtilities.invokeLater轻松完成)

有没有比实现队列解析器更好的方法来调用方法?如果有可以提供一些良好的例子链接?或者队列是否正确处理此任务?如果Queue是正确的方法,如何最好地编码不同的参数?例如"闪存固件"按钮需要通过"文件","将reg A设置为值XY"按钮需要传递Byte ...

2 个答案:

答案 0 :(得分:2)

您可以使用Executors.newSingleThreadExecutor()创建Executor来运行任务。拥有Executor实例后,您可以向其发送Runnable对象。任务将排队,每个任务将在下一个任务开始之前运行完毕,所有任务都使用相同的工作线程。 例如,在您的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);
    });
});

答案 1 :(得分:0)

基于用户“mevqz”回答我做了一个示例代码。它完全正常,似乎是我的问题的好答案。我提供这个示例代码作为精心设计的答案 - 因为我仍然非常努力将它放在一起。另外作为一个新手,我想问一下,如果我正确地得到mevqz提示并且我的代码真的是线程安全的吗?

这里基本上只是一个原始的Backend,我已经实现了调用方法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和Backend引用的包装器。这里似乎不太好,因为Backend实际存储在错误的线程中并且总是传入Runnable.run()。有没有更好的方法将Backend引用直接隐藏到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,带有2个按钮“Open”和“SetRegA”以及用于记录的JTextArea。这只调用BackendRunWrapper中的函数。这里唯一的问题是wether 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();
            }});
    }
}