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

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

 

 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》查找相关笔记

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

如果对作者发布的内容感兴趣,可点击下方关注公众号 钰娘娘知识汇总 查看更多作者文章哦!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/langs/872158.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存