使用泛型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还是手 ...
随机推荐
- github 如何添加项目代码
1.点添加一个resporitory 2.添加的时候一定要选上下面的添加readme这个选项 3.点进去点code就能create file了.贴上代码就行.主要是第二步必须选对
- jquery中innerWidth(),outerWidth(),outerWidth(true)和width()的区别
jquery中innerWidth(),outerWidth(),outerWidth(true)和width()的区别 var a = 元素本身的宽度: width() = a: innerWidt ...
- const与预处理宏#define的区别
在c语言程序设计时,预处理器可以不受限制地建立宏并用它来替代值.因为预处理器只做一些文本替换,宏没有类型检测概念,也没有类型检测功能.所以预处理器的值替换会出现一些小的问题,出现的这些问题,在c++中 ...
- python的介绍和及基本的使用
一 什么是计算机 1 计算机就是由一堆硬件组成的一个机器. 2 硬件的分类: CPU:犹如人类的大脑,运行着需要运行的程序. 内存:将 CPU要运行的内容从硬盘中读取出来,然后CPU在内存里拿内容,只 ...
- hdu-1143(简单dp)
题目链接: 思路:利用前一个状态找到本次状态需要的次数,就是递推. 建立一个二维数组dp[i][j] ,i表示行,j表示多余的格子. 可以分为三种状态dp[i][0], dp[i][1] ,dp[i] ...
- python网页爬虫 spiders_97A-04B
import urllib import urllib.request import bs4 from bs4 import BeautifulSoup as bs import re import ...
- faceswap linux安裝教程
http://www.mamicode.com/info-detail-2602743.html https://blog.csdn.net/sinat_26918145/article/detail ...
- Ubuntu 16.04下安装网络流量分析工具 Wireshark
本文链接地址:https://www.linuxidc.com/Linux/2016-08/134526.htm 切勿用商业用途 sudo apt-add-repository ppa:wiresha ...
- loadrunner提高篇 - 关联技术的经典使用
关联函数是一个查找函数,即是从HTML文件内容中查找需要的值,并将其保存在一个变量或数组中.换一个角度看,关联函数不单单可以匹配一些变化的值,同样可以匹配一些固定的内容,并将其保存到一个数据组,供后续 ...
- (并查集)Travel -- hdu -- 5441(2015 ACM/ICPC Asia Regional Changchun Online )
http://acm.hdu.edu.cn/showproblem.php?pid=5441 Travel Time Limit: 1500/1000 MS (Java/Others) Memo ...