使用泛型SwingWorker与EDT事件分发线程保持通讯
为什么要使用SwingWorker
在swing开发中,如果一个应用程序,执行一些任务,需要大量的时间来完成,比如下载一个大文件或执行一个复杂的数据库查询。
我们假设这些任务是由用户使用一个按钮触发的。在单线程应用程序,用户单击按钮,进入计算的过程,然后等待任务完成之前,所有的事件都在主线程EDT线程进行。
但如果某些任务耗时很长,用户将甚至不能在中途取消任务,应用程序必须响应只有当长任务完成。不幸的是,许多应用程序显着这样的行为和用户感到沮丧,程序仿佛卡死一样。
多线程可以解决这个问题。它使应用程序能够在不同的线程上执行长任务,但多线程带来一个问题,如果需要实时和主线程EDT进行数据交换,该怎么办?我们知道所有的Swing对象都只有一个线程处理,EDT事件调度线程,这导致一个问题:我们不能在EDT事件分派线程以外的其他线程共享对象的对象。
SwingWorker
SwingWorker是一个抽象类,java将它包装好,供方便调用,下面的例子使用字符串对象来通知应用程序。
提供了两个泛型参数。第一个代表返回的对象类型。另一个代表了通知(更新)应用程序的信息的类型,并在下面的例子中高亮显示。
public class MyBlankWorker extends SwingWorker<Integer, String> { @Override
protected Integer doInBackground() throws Exception {
// Start
publish("Start");
setProgress(); // More work was done
publish("More work was done");
setProgress(); // Complete
publish("Complete");
setProgress();
return 1;
} @Override
protected void process(List< String> chunks) {
// Messages received from the doInBackground() (when invoking the publish() method)
}
}
通过setprogress() 设置0和100之间的整数。doinbackground() 用于漫长任务执行。此方法不是由事件调度线程调用的,而是由另一个线程(称为工作线程)。我们可以用publish()方法和/或setprogress()更新进度。调用两个方法都会对事件调度线程的创建新任务,它是工作线程和事件调度线程的线程之间的单向桥。
doinbackground()中调用publish()方法和/或setprogress()必须防止大量的任务发送给事件调度线程,引发洪水事件。
注意:publish()从工作线程调用,而process()由事件调度线程EDT调用。
举例:
输入:
publish("a");
publish("b", "c");
publish("d", "e", "f");
结果就是:
process("a", "b", "c", "d", "e", "f") 最后是一个文件全文检索的异步查询例子,查询某目录下所有的txt文件
import java.io.File;
import java.util.ArrayList;
import java.util.List; import javax.swing.JTextArea;
import javax.swing.SwingWorker; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang.StringUtils; /**
* Searches the text files under the given directory and counts the number of instances a given word is found in these
* file.
*
* @author Albert Attard
*/
public class SearchForWordWorker extends SwingWorker<Integer, String> { private static void failIfInterrupted() throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException("Interrupted while searching files");
}
} /** The word that is searched */
private final String word; /** The directory under which the search occurs. All text files found under the given directory are searched. */
private final File directory; /** The text area where messages are written. */
private final JTextArea messagesTextArea; /**
* Creates an instance of the worker
*
* @param word
* The word to search
* @param directory
* the directory under which the search will occur. All text files found under the given directory are
* searched
* @param messagesTextArea
* The text area where messages are written
*/
public SearchForWordWorker(final String word, final File directory, final JTextArea messagesTextArea) {
this.word = word;
this.directory = directory;
this.messagesTextArea = messagesTextArea;
} @Override
protected Integer doInBackground() throws Exception {
// The number of instances the word is found
int matches = ; /*
* List all text files under the given directory using the Apache IO library. This process cannot be interrupted
* (stopped through cancellation). That is why we are checking right after the process whether it was interrupted or
* not.
*/
publish("Listing all text files under the directory: " + directory);
final List<File> textFiles = new ArrayList<>(FileUtils.listFiles(directory, new SuffixFileFilter(".txt"),
TrueFileFilter.TRUE));
SearchForWordWorker.failIfInterrupted();
publish("Found " + textFiles.size() + " text files under the directory: " + directory); for (int i = , size = textFiles.size(); i < size; i++) {
/*
* In order to respond to the cancellations, we need to check whether this thread (the worker thread) was
* interrupted or not. If the thread was interrupted, then we simply throw an InterruptedException to indicate
* that the worker thread was cancelled.
*/
SearchForWordWorker.failIfInterrupted(); // Update the status and indicate which file is being searched.
final File file = textFiles.get(i);
publish("Searching file: " + file); /*
* Read the file content into a string, and count the matches using the Apache common IO and Lang libraries
* respectively.
*/
final String text = FileUtils.readFileToString(file);
matches += StringUtils.countMatches(text, word); Thread.sleep();
// Update the progress
setProgress((i + ) * / size);
} // Return the number of matches found
return matches;
} @Override
protected void process(final List<String> chunks) {
// Updates the messages text area
for (final String string : chunks) {
messagesTextArea.append(string);
messagesTextArea.append("\n");
}
}
}
JFrame UI类如下
package com.javacreed.examples.swing.worker.part3; import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File; import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingWorker.StateValue; public class Application extends JFrame { /** */
private static final long serialVersionUID = -8668818312732181049L; private Action searchCancelAction;
private Action browseAction; private JTextField wordTextField;
private JTextField directoryPathTextField;
private JTextArea messagesTextArea;
private JProgressBar searchProgressBar; private SearchForWordWorker searchWorker; public Application() {
initActions();
initComponents();
} private void cancel() {
searchWorker.cancel(true);
} private void initActions() {
browseAction = new AbstractAction("Browse") { private static final long serialVersionUID = 4669650683189592364L; @Override
public void actionPerformed(final ActionEvent e) {
final File dir = new File(directoryPathTextField.getText()).getAbsoluteFile();
final JFileChooser fileChooser = new JFileChooser(dir.getParentFile());
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
final int option = fileChooser.showOpenDialog(Application.this);
if (option == JFileChooser.APPROVE_OPTION) {
final File selected = fileChooser.getSelectedFile();
directoryPathTextField.setText(selected.getAbsolutePath());
}
}
}; searchCancelAction = new AbstractAction("Search") { private static final long serialVersionUID = 4669650683189592364L; @Override
public void actionPerformed(final ActionEvent e) {
if (searchWorker == null) {
search();
} else {
cancel();
}
}
};
} private void initComponents() {
setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = ;
constraints.gridy = ;
constraints.insets = new Insets(, , , );
add(new JLabel("Word: "), constraints); wordTextField = new JTextField();
wordTextField.setText("Hello");
constraints = new GridBagConstraints();
constraints.gridx = ;
constraints.gridy = ;
constraints.gridwidth = ;
constraints.insets = new Insets(, , , );
constraints.weightx = ;
constraints.fill = GridBagConstraints.BOTH;
add(wordTextField, constraints); constraints = new GridBagConstraints();
constraints.gridx = ;
constraints.gridy = ;
constraints.insets = new Insets(, , , );
add(new JLabel("Path: "), constraints); directoryPathTextField = new JTextField();
directoryPathTextField.setText("C:\\Users\\Albert\\Work\\JavaCreed\\examples");
constraints = new GridBagConstraints();
constraints.gridx = ;
constraints.gridy = ;
constraints.gridwidth = ;
constraints.insets = new Insets(, , , );
constraints.weightx = ;
constraints.fill = GridBagConstraints.BOTH;
add(directoryPathTextField, constraints); constraints = new GridBagConstraints();
constraints.gridx = ;
constraints.gridy = ;
constraints.insets = new Insets(, , , );
add(new JButton(browseAction), constraints); messagesTextArea = new JTextArea();
messagesTextArea.setEditable(false);
constraints = new GridBagConstraints();
constraints.gridx = ;
constraints.gridy = ;
constraints.gridwidth = ;
constraints.insets = new Insets(, , , );
constraints.weightx = ;
constraints.weighty = ;
constraints.fill = GridBagConstraints.BOTH;
add(new JScrollPane(messagesTextArea), constraints); searchProgressBar = new JProgressBar();
searchProgressBar.setStringPainted(true);
searchProgressBar.setVisible(false);
constraints = new GridBagConstraints();
constraints.gridx = ;
constraints.gridy = ;
constraints.gridwidth = ;
constraints.insets = new Insets(, , , );
constraints.weightx = ;
constraints.fill = GridBagConstraints.BOTH;
add(searchProgressBar, constraints); constraints = new GridBagConstraints();
constraints.gridx = ;
constraints.gridy = ;
constraints.insets = new Insets(, , , );
constraints.weightx = ;
add(new JButton(searchCancelAction), constraints);
} private void search() {
final String word = wordTextField.getText();
final File directory = new File(directoryPathTextField.getText());
messagesTextArea.setText("Searching for word '" + word + "' in text files under: " + directory.getAbsolutePath()
+ "\n");
searchWorker = new SearchForWordWorker(word, directory, messagesTextArea);
searchWorker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(final PropertyChangeEvent event) {
switch (event.getPropertyName()) {
case "progress":
searchProgressBar.setIndeterminate(false);
searchProgressBar.setValue((Integer) event.getNewValue());
break;
case "state":
switch ((StateValue) event.getNewValue()) {
case DONE:
searchProgressBar.setVisible(false);
searchCancelAction.putValue(Action.NAME, "Search");
searchWorker = null;
break;
case STARTED:
case PENDING:
searchCancelAction.putValue(Action.NAME, "Cancel");
searchProgressBar.setVisible(true);
searchProgressBar.setIndeterminate(true);
break;
}
break;
}
}
});
searchWorker.execute();
}
}
入口:
import javax.swing.JFrame;
import javax.swing.SwingUtilities; public class Main {
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
final Application frame = new Application();
frame.setTitle("Swing Worker Demo");
frame.setSize(, );
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
引用:http://www.javacreed.com/swing-worker-example/
使用泛型SwingWorker与EDT事件分发线程保持通讯的更多相关文章
- 深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) [转载]
本系列文章导航 深入浅出Java多线程(1)-方法 join 深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) 深入浅出多线程(3)-Future异步模式以及在JDK1.5Concu ...
- Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread
事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点.今天我们在多线程开发中,穿插进来这个线程.分别从线程的来由.原理和使用方法三个方面来学习事件派发线程. 一 ...
- 从swing分发线程机制上理解多线程[转载]
本文参考了 http://space.itpub.net/13685345/viewspace-374940,原文作者:javagui 在多线程编程当中,总会提到图形编程,比如java中的swing, ...
- 构建一个基于事件分发驱动的EventLoop线程模型
在之前的文章中我们详细介绍过Netty中的NioEventLoop,NioEventLoop从本质上讲是一个事件循环执行器,每个NioEventLoop都会绑定一个对应的线程通过一个for(;;)循环 ...
- mysql 5.6并行复制事件分发机制
并行复制相关线程 在MySQL 5.6并行复制中,当设置set global slave_parallel_workers=2时,共有4个复制相关的线程,如下: +----+------------- ...
- Android开发之漫漫长途 Ⅵ——图解Android事件分发机制(深入底层源码)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- View事件分发
NOTE: 笔记,碎片式内容 控件 App界面的开主要就是使用View,或者称为控件.View既绘制内容又响应输入,输入事件主要就是触摸事件. ViewTree 控件基类为View,而ViewGrou ...
- 转载 【.NET基础】--委托、事件、线程(1) https://www.cnblogs.com/chengzish/p/4559268.html
[.NET基础]--委托.事件.线程(1) 1,委托 是存放方法的指针的清单,也就是装方法的容器 A, 新建winform项目[01委托],项目中添加dg_SayHi.cs 委托类 用于存储方法 ...
- iOS事件分发
前段时间项目有一个需求,要在点击闪屏的时候做一些处理,刚接到这个需求觉得很简单啊,在原有的view上加个button或者手势识别啥的,后面实现的时候发现还是有点坑.无论我在闪屏上面加button还是手 ...
随机推荐
- Linux多线程服务端编程 使用muduo C++网络库 学习笔记 日志log
代码来自陈硕开源代码库 muduo中 地址是https://github.com/chenshuo/muduo #pragma once #include <string> #define ...
- 2018.08.31 bzoj1419 Red is good(期望dp)
描述 桌面上有R张红牌和B张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到1美元,黑牌则付 出1美元.可以随时停止翻牌,在最优策略下平均能得到多少钱. 输入 一行输入两个数R,B,其 ...
- HDU 2161 Primes (素数筛选法)
题意:输入一个数判断是不是素数,并规定2不是素数. 析:一看就很简单吧,用素数筛选法,注意的是结束条件是n<0,一开始被坑了... 不说了,直接上代码: #include <iostrea ...
- 点云库PCL学习
1. 点云的提取 点云的获取:RGBD获取 点云的获取:图像匹配获取(通过摄影测量提取点云数据) 点云的获取:三维激光扫描仪 2. PCL简介 PCL是Point Cloud Library的简称,是 ...
- Robotframework-Appium 之常用API(二)
续接上一文,更多API详细如下: 注:更多官方详情信息见 http://robotframework.org/robotframework/ 28. Name: Install App Source: ...
- 博客搬家到 http://leijun00.github.io
博客园对markdown支持不太好,搬家到 http://leijun00.github.io
- 用idea简单创建web项目——两种方式
最近同学让我教她们用idea创建web项目,于是我用两种方式创建web项目,并整理截图给她们看,一种是用maven创建,一种是不用maven创建,适合菜鸟哈哈~ 方法一:不用maven 1.解压tom ...
- Americans are usually tolerant (Listen speak of Unit 2)
Americans are usually 1) tolerant of non-native speakers who have some 2) trouble understanding Engl ...
- PO Release Final Closed 灾难恢复
今天不小心 Final Closed了一条Po Release,只能通过后台更新数据恢复了. 更新后可接收可匹配,但不保证更新数据有遗漏,慎用. 更新前备份各表数据 UPDATE PO_LINE_LO ...
- Thread in depth 2:Asynchronization and Task
When we want to do a work asynchronously, creating a new thread is a good way. .NET provides two oth ...