**1. SwingWorker**
可以将很耗时的任务交给 SwingWorker类(Swing工作线程)来处理,它可以让后台任务的实现变得简单。而显示UI的任务交给`EventQueue.invokeLater`方法来处理。
* javax.swing.SwingWorker<T, V> 6
* `abstract T doInBackground()`
覆盖这一方法来执行后台的任务并返回这一工作的结果。不允许程序员自己调用。
* `void process(List<V> data)`
覆盖这一方法来处理事件分配线程中的中间进度数据。不允许程序员自己调用。
* `void publish(V... data)`
传递中间进度数据到事件分配线程。只能从`doInBackground`方法内部调用这一方法。
* `void execute()`
为工作器线程的执行预定这个工作器。
* `SwingWorker.StateValue getState()`
得到这个工作器线程的状态,值为PENDING、STARTED、DONE之一。
* 调用`doInBackground`方法来完成耗时的工作,不时地在工作线程器中调用`publish`来报告工作进度。`publish`方法使得`process`方法在事件分配线程中执行处理进度数据。当工作完成时,在事件分配线程中调用`done`方法来完成UI的更新。
* 每当要在工作线程中做一些工作时,构建一个新的工作器(每个工作器对象只能被使用一次)。然后调用`execute`方法。典型的方式是在事件分配线程中调用`execute`方法,但本例子没有这样的需求。
* 假定工作器产生某种类型的结果;因此,SwingWorker<T, V>实现了Future< T>接口,使得可以通过调用Future< T>的`get`方法获取结果。但是由于`get`方法会阻塞,直到结果可用为止,因此不要在调用`execute`方法之后调用它。只在已经知道工作完成时调用它,是最为明智的。典型的是:可以在`done`方法中调用`get`方法。(有时没有调用`get`方法,处理进度数据就是你所需要的)。
* 中间的进度数据,以及最终的结果可以是任意类型。SwingWorker类有3种类型作为类型参数。SwingWorker<T, V>产生类型为T的结果,以及类型为V的进度数据。
* 要取消正在进行的工作,可以调用Future接口的`cancel`方法。当工作被取消时,`get`方法抛出CancellationException异常。
<br/>
**2. 案例演示**
在程序中实现加载文本文件的命令和取消加载过程的命令。应该用一个长的文件来测试这个程序,在gutenberg目录下的crsto10.txt文件为本次测试用的文件。该文件在一个单独的线程中加载。
<br/>
在读取文件的过程中,Open菜单项被禁用,Cancel菜单项为可用;读取完成后,Open菜单为可用,Cancel菜单被禁用,状态行文本置为Done。读取每一行后,状态条中的线性计数器被更新。
<br/>
这个例子展示了后台任务典型的UI活动:
* 在每一个工作单位完成之后,更新UI来显示进度。
* 在整个工作完成之后,对UI做最后的更新。
```java
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import javax.swing.*;
/**
* 该程序演示了一个运行潜在耗时任务的工作线程。
*
* @author Cay Horstmann
* @version 1.11 2015-06-21
*/
public class SwingWorkerTest {
public static void main(String[] args) throws Exception {
//在事件分配线程中显示UI
EventQueue.invokeLater(() -> {
JFrame frame = new SwingWorkerFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
/**
* This frame has a text area to show the contents of a text file, a menu to open a file and
* cancel the opening process, and a status line to show the file loading progress.
*/
class SwingWorkerFrame extends JFrame {
private JFileChooser chooser;
private JTextArea textArea;
private JLabel statusLine;
private JMenuItem openItem;
private JMenuItem cancelItem;
private SwingWorker<StringBuilder, ProgressData> textReader;
public static final int TEXT_ROWS = 20;
public static final int TEXT_COLUMNS = 60;
public SwingWorkerFrame() {
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
textArea = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
add(new JScrollPane(textArea));
statusLine = new JLabel(" ");
add(statusLine, BorderLayout.SOUTH);
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("File");
menuBar.add(menu);
openItem = new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(event -> {
//显示文件选择器对话框
int result = chooser.showOpenDialog(null);
//如果选择了文件,请将其设置为标签的图标
if (result == JFileChooser.APPROVE_OPTION) {
textArea.setText("");
openItem.setEnabled(false); //禁用Open菜单项
textReader = new TextReader(chooser.getSelectedFile());
textReader.execute(); //为工作器线程的执行预定这个工作器
cancelItem.setEnabled(true);
}
});
cancelItem = new JMenuItem("Cancel");
menu.add(cancelItem);
cancelItem.setEnabled(false);
//调用cancel取消当前工作
cancelItem.addActionListener(event -> textReader.cancel(true));
pack(); //更新整个窗口
}
/**
* 记录文件当前的行号和当前的文件
*/
private class ProgressData {
public int number; //记录文件有多少行
public String line; //记录文件的每一行数据
}
/**
* 继续Swing工作线程类,SwingWorker,并重新一些相关的方法
*/
private class TextReader extends SwingWorker<StringBuilder, ProgressData> {
private File file;
private StringBuilder text = new StringBuilder();
public TextReader(File file) {
this.file = file;
}
/**
* 方法在工作线程中执行; 它不涉及Swing组件。该方法不需要程序员调用。
* 将处理文件数据的耗时工作放在doInBackground方法中完成。
* 读取一个文件,每次读取一行,调用publish方法发布当前的行号和当前的文本。
*/
@Override
public StringBuilder doInBackground() throws IOException, InterruptedException {
int lineNumber = 0;
try (Scanner in = new Scanner(new FileInputStream(file), "UTF-8")) {
while (in.hasNextLine()) {
String line = in.nextLine();
lineNumber++;
text.append(line).append("\n");
ProgressData data = new ProgressData();
data.number = lineNumber;
data.line = line;
publish(data); //传递数据,该数据会由下面的process方法接收
Thread.sleep(1); //这只是用来测试的,如果测试取消; 无需在程序中执行此操作
}
}
return text;
}
/**
* 方法在事件分发线程中执行。接触Swing组件。
* 忽略最后一行行号之外的所有行号,然后,把所有的行拼接在一起用于文本区的更新。
*/
@Override
public void process(List<ProgressData> data) {
if (isCancelled()) return; //如果检测到事件分配线程被取消,则什么也不做
StringBuilder b = new StringBuilder();
statusLine.setText("" + data.get(data.size() - 1).number);
for (ProgressData d : data) b.append(d.line).append("\n");
textArea.append(b.toString());
}
/**
* 当工作完成时,调用done来完成UI的更新
*/
@Override
public void done() {
try {
StringBuilder result = get();
textArea.setText(result.toString());
statusLine.setText("Done");
} catch (InterruptedException ex) {
} catch (CancellationException ex) {
textArea.setText("");
statusLine.setText("Cancelled");
} catch (ExecutionException ex) {
statusLine.setText("" + ex.getCause());
}
cancelItem.setEnabled(false);
openItem.setEnabled(true);
}
}
}
```
- 网络通信
- 网络协议
- 端口和套接字
- TCP网络程序
- UDP网络程序
- 多线程聊天室
- 多线程
- 线程相关概念
- 线程实现方式
- 中断线程
- 线程生命周期
- 线程优先级
- 优先级规则
- 案例演示
- 线程同步机制
- 线程同步机制
- synchronized关键字
- ReentrantLock类
- Condition类
- 监视器概念
- volatile关键字
- final变量
- 死锁
- 线程局部变量
- 读/写锁
- 原子类
- 阻塞队列
- 工作规则
- 案例演示
- 常用阻塞队列
- 线程安全集合
- 高效的映射/集/队列
- 并发集视图
- 写数组的拷贝
- Arrays类的并行数组算法
- 同步包装器
- Callable与Future
- 执行器
- 线程池
- 预定执行
- 控制任务组
- Fork-Join框架
- 同步器
- 同步器
- 信号量
- CountDownLatch类
- CyclicBarrier类
- Exchanger类
- SynchronousQueue类
- 线程与Swing
- Swing与线程问题
- 两个原则
- Swing工作线程
- 单一线程规则
- 文件IO
- File类
- 文件输入输出
- ZIP压缩文件
- 集合
- 集合框架
- 集合接口
- 集合实现类
- 线程安全集合
- 集合算法
- 迭代器
- 集合排序
- JDBC
- JDBC是什么
- JDBC-ODBC桥
- JDBC驱动程序类型
- JDBC常用类与接口
- 数据库操作
- 连接数据库
- 增/删/改/查/预处理
- 事务
- 批处理
- commons-dbutils工具
- 安全问题
- Jedis
- 使用Jedis操作Redis数据库
- JSON转换
- 使用连接池
- 案例
- 单例破坏
- 单例定义
- 单例实现方式
- 懒汉式实现单例
- 饿汉式实现单例
- 单例破坏
- 类的单例破坏
- 枚举的单例破坏
- 克隆
- 克隆是什么
- 浅克隆
- 深克隆
- 注解
- 注解是什么
- 三大注解
- 内置注解
- 元注解
- 自定义注解
- NIO
- 相关概念
- BIO/NIO/AIO
- 多线程编程
- 线程同步
- 线程通信
- NIO
- NIO三大核心组件
- NIO网络编程
- NIO文件读写
- AIO
- Java8新特性
- Lambda表达式
- 方法引用
- 函数式接口
- 默认方法
- 什么是默认方法
- 默认方法语法格式
- 多个同名的默认方法问题
- 静态默认方法
- 默认方法实例
- Stream
- Stream是什么
- Stream示例
- Optional容器
- 新的日期时间API
- Base64
- SPI
- SPI是什么
- SPI与API的区别
- 常见场景
- 使用SPI需遵循的约定
- SPI使用步骤