为什么要使用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事件分发线程保持通讯的更多相关文章

  1. 深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) [转载]

    本系列文章导航 深入浅出Java多线程(1)-方法 join 深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) 深入浅出多线程(3)-Future异步模式以及在JDK1.5Concu ...

  2. Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread

    事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点.今天我们在多线程开发中,穿插进来这个线程.分别从线程的来由.原理和使用方法三个方面来学习事件派发线程. 一 ...

  3. 从swing分发线程机制上理解多线程[转载]

    本文参考了 http://space.itpub.net/13685345/viewspace-374940,原文作者:javagui 在多线程编程当中,总会提到图形编程,比如java中的swing, ...

  4. 构建一个基于事件分发驱动的EventLoop线程模型

    在之前的文章中我们详细介绍过Netty中的NioEventLoop,NioEventLoop从本质上讲是一个事件循环执行器,每个NioEventLoop都会绑定一个对应的线程通过一个for(;;)循环 ...

  5. mysql 5.6并行复制事件分发机制

    并行复制相关线程 在MySQL 5.6并行复制中,当设置set global slave_parallel_workers=2时,共有4个复制相关的线程,如下: +----+------------- ...

  6. Android开发之漫漫长途 Ⅵ——图解Android事件分发机制(深入底层源码)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  7. View事件分发

    NOTE: 笔记,碎片式内容 控件 App界面的开主要就是使用View,或者称为控件.View既绘制内容又响应输入,输入事件主要就是触摸事件. ViewTree 控件基类为View,而ViewGrou ...

  8. 转载 【.NET基础】--委托、事件、线程(1) https://www.cnblogs.com/chengzish/p/4559268.html

    [.NET基础]--委托.事件.线程(1)   1,委托 是存放方法的指针的清单,也就是装方法的容器 A, 新建winform项目[01委托],项目中添加dg_SayHi.cs 委托类 用于存储方法 ...

  9. iOS事件分发

    前段时间项目有一个需求,要在点击闪屏的时候做一些处理,刚接到这个需求觉得很简单啊,在原有的view上加个button或者手势识别啥的,后面实现的时候发现还是有点坑.无论我在闪屏上面加button还是手 ...

随机推荐

  1. centos / debian 安装iptables服务

    debian: #使用可参考 https://www.debian.cn/archives/991 #配置文件位于 /etc/iptables #重新配置使用dpkg-reconfigure ipta ...

  2. POJ 2376 Cleaning Shifts (贪心,区间覆盖)

    题意:给定1-m的区间,然后给定n个小区间,用最少的小区间去覆盖1-m的区间,覆盖不了,输出-1. 析:一看就知道是贪心算法的区间覆盖,主要贪心策略是把左端点排序,如果左端点大于1无解,然后, 忽略小 ...

  3. MySQL通过游标来实现通过查询记录集循环

    /*我们有时候会遇到需要对 从A表查询的结果集S_S 的记录 进行遍历并做一些操作(如插入),且这些操作需要的数据或许部分来自S_S集合*//*临时存储过程,没办法,不能直接在查询窗口做这些事.*/d ...

  4. java编程规范-阿里

  5. 使用OpenCV进行相机标定

    1. 使用OpenCV进行标定 相机已经有很长一段历史了.但是,伴随着20世纪后期的廉价针孔照相机的问世,它们已经变成我们日常生活的一种常见的存在.不幸的是,这种廉价是由代价的:显著的变形.幸运的是, ...

  6. MapGIS Mobile开发

    1. 先将Android开发环境配置好(包括Java + Eclipse + Android SDK) 2. 加载API类库(运行MapGIS 10 AndroidSDK.exe可以加载Mobile框 ...

  7. hdu 4004 最大值最小化

    http://acm.hdu.edu.cn/showproblem.php?pid=4004 一条线段长度为L,线段上有n个点,最多选取 m-1 个点,使得包括线段端点在内的相邻点之间的最大距离值最小 ...

  8. Ansible Ad-Hoc命令

    -a:传入模块的参数,不同的模块要传入的参数不同 -B SECOND:当任务放到后台执行异步任务,设置程序运行的超时时间,传入的是一个数值,单位秒 -C:测试该任务能否正常运行,不对被管理主机做出任何 ...

  9. springMVC ModelAndView 作用与功能解析 【转】

    Spring mvc视图机制 所有的web应用的mvc框架都有它定位视图的方式.Spring提供了视图解析器供你在浏览器中显示模型数据,而不必被拘束在特定的视图技术上. Spring的控制器Contr ...

  10. 四则运算 Java (于泽浩,袁浩越)

    GitHub 地址 一. 项目要求 题目 实现一个自动生成小学四则运算题目的命令行程序. 需求(全部完成) 使用 -n 参数控制生成题目的个数 Myapp.exe -n 10 使用 -r 参数控制题目 ...