笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_android

 

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_控件_02

 14.11 线程与 Swing

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_开发语言_03

 前言

本节内容带上了 Swing, 可能很多人看到又不想看了,但是个人建议看看,Android 系统设计本身,页面部分参考的就是这部分的思路,学习这部分,有助于Android 它的设计理念有个初步理解。

Swing 不是线程安全的,多线程操作用户页面,可能导致错误的结果,也可可能造成程序崩溃。

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_开发语言_04

 14.11.1 运行耗时的任务

线程和 Swing 一起使用的两个原则

🍒 如果需要长时间的耗时,需要单独开启任务线程去处理,而非 Swing 所在的 UI(时间分配) 线程处理

🍒 Swing 事件需要在特定的事件分配线程处理,不要在其他线程处理(线程不安全)

这段原则,可能看的稀里糊涂,来点简单的解释

🍒 更新控件的线程,不做耗时操作;

🍒 进行耗时操作的线程,不更新控件。

❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

public class SwingThreadTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new SwingThreadFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}


}

class SwingThreadFrame extends JFrame{
public SwingThreadFrame(){
setTitle("SwingThreadTest");

final JComboBox combo = new JComboBox();
combo.insertItemAt(Integer.MAX_VALUE,0);
combo.setPrototypeDisplayValue(combo.getItemAt(0));
combo.setSelectedIndex(0);

JPanel panel = new JPanel();

JButton goodButton = new JButton("Good");
goodButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new Thread(new GoodWorkerRunnable(combo)).start();
}
});
panel.add(goodButton);
JButton badButton = new JButton("Bad");
badButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new Thread(new BadWorkerRunnable(combo)).start();
}
});
panel.add(badButton);

panel.add(combo);
add(panel);
pack();

}
}

class BadWorkerRunnable implements Runnable{
private JComboBox comboBox;
private Random generator;
public BadWorkerRunnable(JComboBox aCombo){
comboBox = aCombo;
generator = new Random();
}

@Override
public void run() {
try{
while(true){
int i = Math.abs(generator.nextInt());
if(i % 2 == 0) comboBox.insertItemAt(i,0);
else if(comboBox.getItemCount() > 0)
comboBox.removeItemAt(i%comboBox.getItemCount());

Thread.sleep(1);
}
}catch (InterruptedException e){}
}
}

class GoodWorkerRunnable implements Runnable{
private JComboBox comboBox;
private Random generator;
public GoodWorkerRunnable(JComboBox aCombox){
comboBox = aCombox;
generator = new Random();
}

@Override
public void run() {
try{
while(true){
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
int i = Math.abs(generator.nextInt());
if(i % 2 == 0) comboBox.insertItemAt(i,0);
else if(comboBox.getItemCount() > 0)
comboBox.removeItemAt(i%comboBox.getItemCount());
}
});
Thread.sleep(1);
}
}catch (InterruptedException e){}
}
}

❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤

执行结果:

🍒 三个控件:好按钮、坏按钮、下拉框

🍒 初始下拉框加入一个整型最大值

🍒 点击好按钮效果:取一个随机数,随机到偶数插入元素;速记到奇数删除元素(有的话)

🍒 点击坏按钮效果:和好按钮相同,但是没有单独在事件分配线程处理(while(true)耗时),可能产生线程不安全后果

好按钮:

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_ide_05

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_ide_06

坏按钮:

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_ide_07

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_开发语言_08

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_控件_09

 14.11.2 使用 Swing 工作器

自定义Swing工作线程处理:TextReader extends SwingWorker<StringBuilder, ProgressData>();

泛型解释:返回值类型/事件分配线程参数类型

方法:doInBackGround() 第一步,工作线程处理,可进行耗时操作,不能更新页面,调用publish(data),把参数传给 process() 处理

方法:process(),doInBackGround() 第二步,调用 publish() 之后,轮到事件分配线程处理,此时通过给定的数据进行 Swing 控件更新,其中参数类型,为类种传入的第二个泛型参数的类型

方法:done() 第三步,doInBackGround() 全部处理完毕后会调用此方法,可通过get() 获取 doInBackground() 给定的返回值

另外:cancel(true) 可进行取消、中断操作

开始执行:exiecute();

如果你学过Android, 会发现里面的AsyncTask 和这个 SwingWorker 几乎一样的,非常形似。

原书作者给出的测试代码:

❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;

public class SwingWorkerTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new SwingWorkerFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

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 WIDTH = 450;
public static final int HEIGHT = 350;

public SwingWorkerFrame(){
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
textArea = new JTextArea();
add(new JScrollPane(textArea));
add(new JScrollPane(textArea));
setSize(WIDTH,HEIGHT);

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(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int result = chooser.showOpenDialog(null);
if(result == JFileChooser.APPROVE_OPTION){
textArea.setText("");
openItem.setEnabled(false);
textReader = new TextReader(chooser.getSelectedFile());
textReader.execute();
cancelItem.setEnabled(true);
}
}
});

cancelItem = new JMenuItem("Cancel");
menu.add(cancelItem);
cancelItem.setEnabled(false);
cancelItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textReader.cancel(true);
}
});

}

public class ProgressData{
public int number;
private String line;
}
private class TextReader extends SwingWorker<StringBuilder, ProgressData>{
private File file;
private StringBuilder text = new StringBuilder();
public TextReader(File file){
this.file = file;
}

@Override
protected StringBuilder doInBackground() throws IOException,InterruptedException {
int lineNumber = 0;
Scanner in = new Scanner(new FileInputStream(file));
while(in.hasNextLine()){
String line = in.nextLine();
lineNumber++;
text.append(line);
text.append("\n");
ProgressData data = new ProgressData();
data.number = lineNumber;
data.line = line;
publish(data);
Thread.sleep(1);
}
return text;
}

@Override
protected 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);
b.append("\n");
}
textArea.append(b.toString());
}

@Override
protected void done() {
try{
StringBuilder result = get();
textArea.setText(result.toString());
statusLine.setText("Done");
}catch (InterruptedException e){

}catch (CancellationException ex){
textArea.setText("");
statusLine.setText("Canceled");
}catch (ExecutionException ex){
statusLine.setText(""+ex.getCause());
}
cancelItem.setEnabled(false);
openItem.setEnabled(true);
}
}
}

❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤

执行结果:

🍒 左上角菜单可选择读入文件

🍒 左下角读入文件显示行数,文本域会显示读入内容

🍒 读取完毕后会显示Done

🍒 如果在读取中选择了Cancel,则内容会情况,左下角显示Cancel

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_android_10

笔记 第14章 多线程(15)Swing执行耗时任务和Swing工作器_开发语言_11

 相关内容:选择 《Java核心技术 卷1》查找相关笔记

评论🌹点赞👍收藏✨关注👀,是送给作者最好的礼物,愿我们共同学习,一起进步

公众号 钰娘娘知识汇总