how2j.cn

本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频



20分34秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)



步骤 1 : 三种线程   
步骤 2 : 事件调度线程是单线程的   
步骤 3 : 初始化线程   
步骤 4 : 事件调度线程   
步骤 5 : 长耗时任务线程   
步骤 6 : 练习-查找文件内容   
步骤 7 : 答案-查找文件内容   

步骤 1 :

三种线程

在Swing程序的开发中,需要建立3种线程的概念
1. 初始化线程
初始化线程用于创建各种容器组件并显示他们,一旦创建并显示,初始化线程的任务就结束了。

2. 事件调度线程
通过事件监听的学习,我们了解到Swing是一个事件驱动的模型,所有和事件相关的操作都放是放在事件调度线程 (Event Dispatch)中进行的。比如点击一个按钮,对应的ActionListener.actionPerformed 方法中的代码,就是在事件调度线程 Event Dispatch Thread中执行的。

3. 长耗时任务线程
有时候需要进行一些长时间的操作,比如访问数据库,文件复制,连接网络,统计文件总数等等。 这些操作就不适合放在事件调度线程中进行,因为占用时间久了,会让使用者感觉界面响应很卡顿。 为了保持界面响应的流畅性,所有长耗时任务都应该放在专门的 长耗时任务线程中进行
步骤 2 :

事件调度线程是单线程的

在开始讲解这3种线程之前, 要建立一个概念: 事件调度线程是单线程的。
为什么呢?
这是因为 Swing里面的各种组件类,比如JTextField,JButton 都不是线程安全的,这就意味着,如果有多个线程,那么同一个JTextField的setText方法,可能会被多个线程同时调用,这会导致同步问题以及错误数据的发生。

如果把组件类设计成为线程安全的,由于Swing事件调度的复杂性,就很有可能导致死锁的发生。

为了规避同步问题,以及降低整个Swing设计的复杂度,提高Swing的相应速度,Swing中的 事件调度线程被设计成为了单线程模式,即只有一个线程在负责事件的响应工作。


参考阅读:线程安全的概念
步骤 3 :

初始化线程

如代码所示,同时我们在初始化一个图形界面的时候,都会直接在主方法的主线程里,直接调用如下代码来进行初始化

new TestFrame().setVisible(true);

如果是小程序这没有什么问题,如果是复杂的程序就有可能产生问题了。因为这里有两个线程在同时访问组件:1. 主线程 2. 事件调度线程。 如果是复杂的图形界面程序,就有可能出现这两个线程同时操作的情况,导致同步问题的产生。

为了规避这个问题的产生,创建和显示界面的工作,最好也交给事件调度线程,这样就保证了只有一个线程在访问这些组件


SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestFrame().setVisible(true);
}
});

像这样,new TestFrame().setVisible(true); 这段代码就是在事件调度线程中执行了。

还可以使用SwingUtilities.isEventDispatchThread()来判断当前线程是否是事件调度线程
package gui; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class TestGUI { public static void main(String[] args) { new TestFrame().setVisible(true); // SwingUtilities.invokeLater(new Runnable() { // public void run() { // new TestFrame().setVisible(true); // } // }); } static class TestFrame extends JFrame { public TestFrame() { setTitle("LoL"); setSize(400, 300); setLocation(200, 200); setLayout(null); JButton b = new JButton("一键秒对方基地挂"); b.setBounds(50, 50, 280, 30); add(b); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); System.out.println("当前线程是否是 事件调度线程: " + SwingUtilities.isEventDispatchThread()); } } }
步骤 4 :

事件调度线程

按钮监听 中的代码为例,ActionListener.actionPerformed 中的代码,就是事件调度线程执行的。

可以借助SwingUtilities.isEventDispatchThread() 确认,是事件调度线程在执行相应的代码

那么事件调度线程又是如何去执行这段代码的呢? 这就不用你操心啦, 要解释这个问题,就需要剖析整个Swing的框架,就不是本章节所能展示的内容啦
事件调度线程
package gui; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; public class TestGUI { public static void main(String[] args) { JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(580, 200); f.setLayout(null); final JLabel l = new JLabel(); ImageIcon i = new ImageIcon("e:/project/j2se/shana.png"); l.setIcon(i); l.setBounds(50, 50, i.getIconWidth(), i.getIconHeight()); JButton b = new JButton("隐藏图片"); b.setBounds(150, 200, 100, 30); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { l.setVisible(false); System.out.println("当前使用的是事件调度线程:" + SwingUtilities.isEventDispatchThread()); } }); f.add(l); f.add(b); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
步骤 5 :

长耗时任务线程

有时候需要执行长耗时任务,比如数据库查询,文件复制,访问网络等等。

而这些操作一般都会在事件响应后发起,就会自动进入事件调度线程。 而事件调度线程又是单线程模式,其结果就会是在执行这些长耗时任务的时候,界面就无响应了

如图所示,当点击第一个按钮的时候,会在其中进行一个5秒钟的任务,这个期间,第一个按钮会保持按下状态,其他按钮也无法点击,出现了无响应了状态。

为了解决这个问题,Swing提供了一个SwingWorker类来解决。 SwingWorker是一个抽象类,为了使用,必须实现方法 doInBackground,在doInBackground中,就可以编写我们的任务,然后执行SwingWorker的execute方法,放在专门的工作线程中去运行。

SwingWorker worker = new SwingWorker() {
protected Object doInBackground() throws Exception {
//长耗时任务
return null;
}
};
worker.execute();

SwingWorker又是如何工作的呢?
当SwingWorker执行execute的时候,调用默认有10根线程的线程池,执行doInBackground中的代码,通过如下代码,可以获知执行当前SwingWorder的线程名称

System.out.println("执行这个SwingWorder的线程是:" + Thread.currentThread().getName());
长耗时任务线程
package gui; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingWorker; public class TestGUI { public static void main(String[] args) { JFrame f = new JFrame("LoL"); f.setSize(300, 300); f.setLocation(200, 200); f.setLayout(new FlowLayout()); JButton b1 = new JButton("在事件调度线程中执行长耗时任务"); JButton b2 = new JButton("使用SwingWorker执行长耗时任务"); JLabel l = new JLabel("任务执行结果"); f.add(b1); f.add(b2); f.add(l); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); b1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { l.setText("开始执行完毕"); try { Thread.sleep(5000); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } l.setText("任务执行完毕"); } }); b2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { System.out.println("执行这个SwingWorder的线程是:" + Thread.currentThread().getName()); l.setText("开始执行完毕"); try { Thread.sleep(5000); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } l.setText("任务执行完毕"); return null; } }; worker.execute(); } }); f.setVisible(true); } }
步骤 6 :

练习-查找文件内容

Or  姿势不对,事倍功半! 点击查看做练习的正确姿势
参考 练习-查找文件内容中的做法,使用swing来完成这个功能。

查找文件内容本身是一个比较耗时的任务,采用长耗时任务线程的手段,开发这个功能。
练习-查找文件内容
步骤 7 :

答案-查找文件内容

在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

5分18秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)


package gui; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileReader; import java.io.IOException; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; public class TestGUI { public static void main(String[] args) { SwingUtilities.invokeLater(()->new SearchFrame().setVisible(true)); } static class SearchFrame extends JFrame { JLabel lLocation = new JLabel("查询目录"); JLabel lSearch = new JLabel("文件内容"); JTextField tfLocation = new JTextField("e:/project"); JTextField tfSearch = new JTextField("java"); JButton bSubmit = new JButton("搜索"); private void unfreeze() { bSubmit.setText("搜索"); bSubmit.setEnabled(true); tfLocation.setEnabled(true); tfSearch.setEnabled(true); } private void freeze() { bSubmit.setText("正在搜索"); bSubmit.setEnabled(false); tfLocation.setEnabled(false); tfSearch.setEnabled(false); } SearchFrame() { int gap = 50; this.setLayout(null); JPanel pInput = new JPanel(); JPanel pSubmit = new JPanel(); pInput.setLayout(new GridLayout(2, 2, gap, gap)); pInput.add(lLocation); pInput.add(tfLocation); pInput.add(lSearch); pInput.add(tfSearch); pSubmit.add(bSubmit); pInput.setBounds(50, 20, 200, 100); pSubmit.setBounds(0, 130, 300, 150); this.add(pInput); this.add(pSubmit); this.setSize(300, 200); this.setLocationRelativeTo(null); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); bSubmit.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { foundCount = 0; String location = tfLocation.getText(); String search =tfSearch.getText(); if(0==location.length()){ JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空"); tfLocation.grabFocus(); return; } if(0==search.length()){ JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空"); tfSearch.grabFocus(); return; } File folder = new File(location); if(!folder.exists()){ JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不存在"); tfLocation.grabFocus(); return; } if(!folder.isDirectory()){ JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不是一个文件夹"); tfLocation.grabFocus(); return; } freeze(); SwingWorker worker = new SwingWorker(){ @Override protected Object doInBackground() throws Exception { //work here search(folder,search); JOptionPane.showMessageDialog(SearchFrame.this, "总共找到满足条件的文件: " + foundCount + " 个"); unfreeze(); return null; } }; worker.execute(); } }); } } private static int foundCount = 0; public static void search(File file, String search) { if (file.isFile()) { if(file.getName().toLowerCase().endsWith(".java")){ String fileContent = readFileConent(file); if(fileContent.contains(search)){ foundCount++; } } } if (file.isDirectory()) { File[] fs = file.listFiles(); for (File f : fs) { search(f, search); } } } public static String readFileConent(File file){ try (FileReader fr = new FileReader(file)) { char[] all = new char[(int) file.length()]; fr.read(all); return new String(all); } catch (IOException e) { e.printStackTrace(); return null; } } }


HOW2J公众号,关注后实时获知布最新的教程和优惠活动,谢谢。


问答区域    
2017-04-07 freeze() 和 unfreeze ()这两个函数什么意思,为什么要那样做
baymax04



freeze() 和 unfreeze ()这两个函数什么意思 private void unfreeze() { bSubmit.setText("搜索"); bSubmit.setEnabled(true); tfLocation.setEnabled(true); tfSearch.setEnabled(true); } private void freeze() { bSubmit.setText("正在搜索"); bSubmit.setEnabled(false); tfLocation.setEnabled(false); tfSearch.setEnabled(false); }
package gui;
  
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
  
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
  
public class TestGUI {
  
    public static void main(String[] args) {
        SwingUtilities.invokeLater(()->new SearchFrame().setVisible(true));
    }
  
    static class SearchFrame extends JFrame {
        JLabel lLocation = new JLabel("查询目录");
        JLabel lSearch = new JLabel("文件内容");
  
        JTextField tfLocation = new JTextField("e:/project");
        JTextField tfSearch = new JTextField("java");
  
        JButton bSubmit = new JButton("搜索");
  
        private void unfreeze() {
            bSubmit.setText("搜索");
            bSubmit.setEnabled(true);
            tfLocation.setEnabled(true);
            tfSearch.setEnabled(true);
              
        }
  
        private void freeze() {
            bSubmit.setText("正在搜索");
            bSubmit.setEnabled(false);
            tfLocation.setEnabled(false);
            tfSearch.setEnabled(false);
        }
          
        SearchFrame() {
            int gap = 50;
            this.setLayout(null);
  
            JPanel pInput = new JPanel();
            JPanel pSubmit = new JPanel();
  
            pInput.setLayout(new GridLayout(2, 2, gap, gap));
            pInput.add(lLocation);
            pInput.add(tfLocation);
            pInput.add(lSearch);
            pInput.add(tfSearch);
  
            pSubmit.add(bSubmit);
  
            pInput.setBounds(50, 20, 200, 100);
            pSubmit.setBounds(0, 130, 300, 150);
  
            this.add(pInput);
            this.add(pSubmit);
  
            this.setSize(300, 200);
            this.setLocationRelativeTo(null);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            bSubmit.addActionListener(new ActionListener() {
  
                @Override
                public void actionPerformed(ActionEvent e) {
                    foundCount = 0;
                    String location = tfLocation.getText();
                    String search =tfSearch.getText();
                    if(0==location.length()){
                        JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空");
                        tfLocation.grabFocus();
                        return;
                    }
                    if(0==search.length()){
                        JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空");
                        tfSearch.grabFocus();
                        return;
                    }
                      
                    File folder = new File(location);
                    if(!folder.exists()){
                        JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不存在");
                        tfLocation.grabFocus();
                        return;
                    }
                    if(!folder.isDirectory()){
                        JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不是一个文件夹");
                        tfLocation.grabFocus();
                        return;
                    }
                      
                    freeze();
                      
                    SwingWorker worker = new SwingWorker(){
  
                        @Override
                        protected Object doInBackground() throws Exception {
                            //work here
                              
                            search(folder,search);
                            JOptionPane.showMessageDialog(SearchFrame.this, "总共找到满足条件的文件: " + foundCount + " 个");
                            unfreeze();
                            return null;
                              
                        }
  
                    };
                      
                    worker.execute();
                }
            });
  
        }
    }
      
    private static int foundCount = 0;
    public static void search(File file, String search) {
        if (file.isFile()) {
            if(file.getName().toLowerCase().endsWith(".java")){
                String fileContent = readFileConent(file);
                if(fileContent.contains(search)){
                    foundCount++;
                }
            }
        }
        if (file.isDirectory()) {
            File[] fs = file.listFiles();
            for (File f : fs) {
                search(f, search);
            }
        }
    }
       
    public static String readFileConent(File file){
        try (FileReader fr = new FileReader(file)) {
            char[] all = new char[(int) file.length()];
            fr.read(all);
            return new String(all);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
   
    }   
}

							


1 个答案

how2j 答案时间:2017-04-08
使得按钮不可点击




答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2017-02-26 程序执行的结果不对
feiniao



不知道为何结果总是不对
package how2j;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
 
public class TestGui {
	static int i = 0;
    public static void main(String[] args){
        JFrame f1 = new JFrame("LoL");
        f1.setSize(1000, 1000);
        f1.setLocation(200, 200);
        f1.setLayout(new FlowLayout());
        JTextField tfName = new JTextField("");
        tfName.setText("请输入要查找的字符串");
        tfName.setPreferredSize(new Dimension(200,100));
        final JFileChooser fc = new JFileChooser();
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        final String search = tfName.getText();
        
        final JButton b1 = new JButton("开始检索");
        
        
        
    
        
        b1.addActionListener(new ActionListener() {
        	 
            @Override
            public void actionPerformed(ActionEvent e) {
                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
 
                    @Override
                    protected Void doInBackground() throws Exception {
                        System.out.println("执行这个SwingWorder的线程是:" + Thread.currentThread().getName());
                        b1.setText("正在检索");
                        TestGui.search(fc.getSelectedFile(),search);
                        JOptionPane.showMessageDialog(null,String.valueOf(i), "标题",JOptionPane.PLAIN_MESSAGE); 
                        return null;
                    }
                };
                worker.execute();
 
            }
        });
 
        f1.add(tfName);
        f1.add(fc);
        f1.add(b1);
        f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
      
 
        f1.setVisible(true);
    }
    public static void SearchFile(File file,String search){
    	 
        
    	String fileContent = readFileConent(file);
        while(fileContent.contains(search)){
        	i++;
            
             
        } 
    }
    public static String readFileConent(File file){
        try (FileReader fr = new FileReader(file)) {
            char[] all = new char[(int) file.length()];
            fr.read(all);
            return new String(all);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
  
    }     
    
    public static void search(File file, String search) {
        
        if (file.isFile()) {
            if(file.getName().toLowerCase().endsWith(".java")){
                SearchFile(file, search);
                
            }
        }
        if (file.isDirectory()) {
            File[] fs = file.listFiles();
            for (File f : fs) {
                search(f, search);
            }
        }
    }
            
       
        
     
}
    

							


2 个答案

how2j 答案时间:2017-03-01
修改过后,已经可用了吗?

feiniao 答案时间:2017-02-28
package how2j; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileReader; import java.io.IOException; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.SwingWorker; public class TestGUI1 { static int i = 0; public static void main(String[] args){ JFrame f1 = new JFrame("LoL"); f1.setSize(1000, 1000); f1.setLocation(200, 200); f1.setLayout(new FlowLayout()); final JTextField tfName = new JTextField(""); tfName.setText("请输入要查找的字符串"); tfName.setPreferredSize(new Dimension(200,100)); final JFileChooser fc = new JFileChooser(); fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); final JButton b1 = new JButton("开始检索"); b1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @SuppressWarnings("rawtypes") SwingWorker worker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { System.out.println("执行这个SwingWorder的线程是:" + Thread.currentThread().getName()); b1.setText("正在检索"); System.out.println(fc.getSelectedFile().toString()); System.out.println(tfName.getText()); search(fc.getSelectedFile(),tfName.getText()); System.out.printf("%d",i); JOptionPane.showMessageDialog(null,String.valueOf(i), "标题",JOptionPane.PLAIN_MESSAGE); return null; } }; worker.execute(); } }); f1.add(tfName); f1.add(fc); f1.add(b1); f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f1.setVisible(true); } public static void SearchFile(File file,String search){ String fileContent = readFileConent(file); if(fileContent.contains(search)){ i++; } } public static String readFileConent(File file){ try (FileReader fr = new FileReader(file)) { char[] all = new char[(int) file.length()]; fr.read(all); return new String(all); } catch (IOException e) { e.printStackTrace(); return null; } } public static void search(File file, String search) { if (file.isFile()) { if(file.getName().toLowerCase().endsWith(".java")){ SearchFile(file, search); } } if (file.isDirectory()) { File[] fs = file.listFiles(); for (File f : fs) { search(f, search); } } } }




答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到









提问之前请登陆
关于 JAVA 中级-图形界面-Swing中的线程 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 620943819
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: http://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图