为什么要使用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. 配置tomcat server.xml 文件 ,虚拟路径

    <Context path="/web" docBase="D:\workspace\web\src\main\webapp"  reloadable=& ...

  2. Python 的stat 模块

    #!/usr/bin/env python#-*- encoding:UTF-8 -*- import os,time,stat fileStats = os.stat ( 'test.txt' )  ...

  3. DataStage 九、数据交换到MySQL以及乱码问题

    DataStage序列文章 DataStage 一.安装 DataStage 二.InfoSphere Information Server进程的启动和停止 DataStage 三.配置ODBC Da ...

  4. pthread_once 和 pthread_key

    http://blog.csdn.net/rickyguo/article/details/6259410 一次性初始化 有时候我们需要对一些posix变量只进行一次初始化,如线程键(我下面会讲到). ...

  5. Devexpress VCL Build v2014 vol 14.2.6 发布

    终于支持XE8 了.需要这么长时间吗? New Major Features in 14.2 What's New in VCL Products 14.2 Feature Highlights To ...

  6. 2018.10.18 NOIP训练 [SCOI2018]Pipi 酱的日常(线段树)

    传送门 线段树好题啊. 题目要求的是sum−a−b−c+maxsum-a-b-c+maxsum−a−b−c+max{∣a+v∣+∣b+v∣+∣c+v∣|a+v|+|b+v|+|c+v|∣a+v∣+∣b ...

  7. Part 6 - Class-Based Views(21-26)

    https://github.com/sibtc/django-beginners-guide/tree/v0.6-lw urlpatterns = [ views.PostUpdateView.as ...

  8. SpringMVC零碎笔记

    在web.xml里可以配置webapp的默认首页,格式如下: <welcome-file-list> <welcome-file>index.html</welcome- ...

  9. 批处理(bat)的注释方法

    前面两个批处理教程的转贴,写得实在是太好了,内容详实,语言风趣,举例清晰.说实话,原作者幽默的文风用来写 批处理教程,而不是写文学小说,实在是有些屈才,但这样优秀的教程,用在枯燥乏味的程序事业里,确实 ...

  10. colorbox在android上由于高度太高无法scroll问题

    首先看问题截图: 我们可以看到弹出的colorbox页面的高度已经到了下面,你根本看不到"mistake cross"<=>"X". 我测试了iph ...