为什么要使用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. app怎么测试性能

    性能测试一般来说 都是代码能力相对薄弱的测试人员 进阶的一个方向:但是当你成为一个真正的全栈人才的时候你就不得不学习代码: APP 或者安卓手机 或者iOS  一本测试他的性能的话都是采用:手机安装一 ...

  2. 2018.09.16 atcoder Garbage Collector(贪心)

    传送门 昨晚打比赛的时候不是很机智啊. 这道题贪心就能过了. 我们可以发现一个明显的结论,每次选的垃圾的距离从大到小排序之后,每个距离对答案的贡献的系数是5,5,7,9,11-也就是最远的是5,其余都 ...

  3. IntelliJ IDEA 2017版 使用笔记(四) 模板 live template自定义设置;IDE快捷键使用

    1.File ---> setting ---->Live Template                2.添加模板 3.添加模板组 4.模板组命名 5.填写配置 6.Template ...

  4. Java文件复制删除操作合集

    import java.io.*; public class FileOperate { public FileOperate() { } /** * 新建目录 * @param folderPath ...

  5. MemCachedClient 节点失效时的处理

    引入jar包 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3 ...

  6. 一次简单完整的自动化登录测试-基于python+selenium进行cnblog的自动化登录测试

    Web登录测试是很常见的测试,手动测试大家再熟悉不过了,那如何进行自动化登录测试呢!本文就基于python+selenium结合unittest单元测试框架来进行一次简单但比较完整的cnblog自动化 ...

  7. hdu1158 Employment Planning 2016-09-11 15:14 33人阅读 评论(0) 收藏

    Employment Planning Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  8. 一个简单 Go Web MVC 框架实现思路

    需要的知识点 为了防止你的心里不适,需要以下知识点: Go 基本知识 Go 反射的深入理解 使用过框架 Go Web 服务器搭建 package main import ( "fmt&quo ...

  9. svg transform

    看了这个页面的说明:http://www.2cto.com/kf/201301/186980.html 总结如下:transform包括:translate(tx,ty),scale(sx,sy),r ...

  10. inline函数的作用

    (一)inline函数(摘自C++ Primer的第三版) 在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联. inline int min(int first, int ...