您好我知道有很多问题和相关答案。就像从后端使用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 ...
答案 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();
}});
}
}