14.11 线程与 Swing 前言
本节内容带上了 Swing, 可能很多人看到又不想看了,但是个人建议看看,Android 系统设计本身,页面部分参考的就是这部分的思路,学习这部分,有助于Android 它的设计理念有个初步理解。
Swing 不是线程安全的,多线程 *** 作用户页面,可能导致错误的结果,也可可能造成程序崩溃。
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.11.2 使用 Swing 工作器自定义Swing工作线程处理:TextReader extends SwingWorker
泛型解释:返回值类型/事件分配线程参数类型
方法: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 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{
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 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
相关内容:选择 《Java核心技术 卷1》查找相关笔记
评论🌹点赞👍收藏✨关注👀,是送给作者最好的礼物,愿我们共同学习,一起进步
如果对作者发布的内容感兴趣,可点击下方关注公众号 钰娘娘知识汇总 查看更多作者文章哦!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)