本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频
20分34秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器 步骤 1 : 三种线程 步骤 2 : 事件调度线程是单线程的 步骤 3 : 初始化线程 步骤 4 : 事件调度线程 步骤 5 : 长耗时任务线程 步骤 6 : 练习-查找文件内容 步骤 7 : 答案-查找文件内容
在Swing程序的开发中,需要建立3种线程的概念
1. 初始化线程 初始化线程用于创建各种容器,组件并显示他们,一旦创建并显示,初始化线程的任务就结束了。 2. 事件调度线程 通过事件监听的学习,我们了解到Swing是一个事件驱动的模型,所有和事件相关的操作都放是放在事件调度线程 (Event Dispatch)中进行的。比如点击一个按钮,对应的ActionListener.actionPerformed 方法中的代码,就是在事件调度线程 Event Dispatch Thread中执行的。 3. 长耗时任务线程 有时候需要进行一些长时间的操作,比如访问数据库,文件复制,连接网络,统计文件总数等等。 这些操作就不适合放在事件调度线程中进行,因为占用时间久了,会让使用者感觉界面响应很卡顿。 为了保持界面响应的流畅性,所有长耗时任务都应该放在专门的 长耗时任务线程中进行
在开始讲解这3种线程之前, 要建立一个概念: 事件调度线程是单线程的。
为什么呢? 这是因为 Swing里面的各种组件类,比如JTextField,JButton 都不是线程安全的,这就意味着,如果有多个线程,那么同一个JTextField的setText方法,可能会被多个线程同时调用,这会导致同步问题以及错误数据的发生。 如果把组件类设计成为线程安全的,由于Swing事件调度的复杂性,就很有可能导致死锁的发生。 为了规避同步问题,以及降低整个Swing设计的复杂度,提高Swing的相应速度,Swing中的 事件调度线程被设计成为了单线程模式,即只有一个线程在负责事件的响应工作。 参考阅读:线程安全的概念
如代码所示,同时我们在初始化一个图形界面的时候,都会直接在主方法的主线程里,直接调用如下代码来进行初始化
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());
}
}
}
以 按钮监听 中的代码为例,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秒钟的任务,这个期间,第一个按钮会保持按下状态,其他按钮也无法点击,出现了无响应了状态。 为了解决这个问题,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);
}
}
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频
5分18秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器 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公众号,关注后实时获知最新的教程和优惠活动,谢谢。
问答区域
2022-01-06
练习 - swing 查找文件内容
回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
2020-10-17
设置主程序窗口
2 个答案
hoolich 跳转到问题位置 答案时间:2022-05-26 //参考 练习-查找文件内容中的做法,使用swing来完成这个功能。
//
//查找文件内容本身是一个比较耗时的任务,采用长耗时任务线程的手段,开发这个功能。
package gui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class TestGUI18 {
public static int cnt=0;
/**
* @param file 查找的目录
* @param search 查找的字符串
*/
public static int search(File file, String search) {
if (file.isFile()) {
if(file.getName().toLowerCase().endsWith(".java")){
String fileContent = readFileConent(file);
if(fileContent.contains(search)){
// System.out.printf("找到子目标字符串%s,在文件:%s%n",search,file);
cnt++;
}
}
}
if (file.isDirectory()) {
File[] fs = file.listFiles();
for (File f : fs) {
search(f, search);
}
}
return cnt;
}
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 main(String[] args) {
JFrame f = new JFrame("LoL");
f.setSize(400, 300);
f.setLocation(200, 200);
JPanel pInput = new JPanel();
int gap=10;
pInput.setLayout(new GridLayout(1, 4, gap, gap));
pInput.setBounds(gap, gap, 375, 120);
JLabel source_file_name = new JLabel("源文件地址");
JTextField source_file = new JTextField("");
JLabel source_file_content = new JLabel("源文件内容");
JTextField source_content = new JTextField("");
pInput.add(source_file_name);
pInput.add(source_file);
pInput.add(source_file_content);
pInput.add(source_content);
JButton b = new JButton("开始查找");
f.add(pInput);
f.add(b);
f.setLayout(new FlowLayout());
b.addActionListener(new ActionListener() {
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());
String srcPath = source_file.getText();
String content = source_content.getText();
File file = new File(srcPath);
int count = search(file, content);
JOptionPane.showMessageDialog(f, "查找到" + count + "个文件");
return null;
}
};
worker.execute();
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
// File folder =new File("e:\\project");
// search(folder,"Magic");
}
}
xian666 跳转到问题位置 答案时间:2021-05-11 比如TestJFrame继承了Jframe,,在构造方法里面就可以省略主程序名,因为默认是this,但这个类就实现了Jframe,就默认指向了Jframe
回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
2020-05-18
简单就好
2019-09-25
代码没问题,但我增加了一个键盘监听却没有反应,什么原因?
2019-03-21
练习题130到134行的if
提问太多,页面渲染太慢,为了加快渲染速度,本页最多只显示几条提问。还有 4 条以前的提问,请 点击查看
提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
|