Swing中的线程

步骤 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() 确认,是事件调度线程在执行相应的代码

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);
}
}

练习: 查找文件内容

(查找文件内容本身是一个比较耗时的任务,采用长耗时任务线程的手段,开发这个功能)

Java自学-图形界面 Swing中的线程的更多相关文章

  1. Java自学-图形界面 容器

    Swing 的容器 JFrame和JDialog java的图形界面中,容器是用来存放 按钮,输入框等组件的. 窗体型容器有两个,一个是JFrame,一个是JDialog 步骤 1 : JFrame ...

  2. Java自学-图形界面 事件监听

    Swing 如何进行事件监听 示例 1 : 按钮监听 创建一个匿名类实现ActionListener接口,当按钮被点击时,actionPerformed方法就会被调用 package gui; imp ...

  3. AWT提供了Java Applet 和Java Application中可用的用户图形界面 GUI 中的基本组件

    AWT提供了Java Applet 和Java Application中可用的用户图形界面 GUI 中的基本组件( component s). 由于Java是一种独立于平台的 程序设计语言 ,但GUI ...

  4. 【计项02组01号】Java版图形界面计算器

    Java版图形界面计算器1.0版本 项目分析[1.0] 组成部分 代码结构 (1)窗口的创建 在<JDK 核心 API>中我们提到,创建一个窗口需要使用 JFrame 类.在本实验中,我们 ...

  5. Java的图形界面依然是跨平台的

    Awt:抽象窗口工具箱,它由三部分组成: ①组件:界面元素: ②容器:装载组件的容器(例如窗体): ③布局管理器:负责决定容器中组件的摆放位置. 图形界面的应用分四步: ① 选择一个容器: ⑴wind ...

  6. JAVA与图形界面开发(Applet应用程序、AWT库、Swing)

    Applet 1)简单说,Applet就是嵌入到网页中的小程序,Java代码. 2)编写Applet程序,要继承JApplet类,并根据自己需要覆写相关方法(init.start.stop.destr ...

  7. Java GUI图形界面开发工具

    Applet 应用程序     一种可以在 Web 浏览器中执行的小程序,扩展了浏览器中的网页功能. 缺: 1.需要下载 Applet 及其相关文件 2.Applet 的功能是受限制的 优: 3.无需 ...

  8. Java学习笔记——可视化Swing中JTable控件绑定SQL数据源的两种方法

    在 MyEclipse 的可视化 Swing 中,有 JTable 控件. JTable 用来显示和编辑常规二维单元表. 那么,如何将 数据库SQL中的数据绑定至JTable中呢? 在这里,提供两种方 ...

  9. Java实现图形界面的三部曲及IDE中的窗口设计

    设计和实现图形用户界面的工作主要有以下几点: • (1)创建组件(Component) • 创建组成界面的各种元素,如按钮.文本框等.• (2)指定布局(Layout) • 根据具体需要排列它们的位置 ...

随机推荐

  1. qemu 如何退出qemu

    如何退出qemu ctrl + a 放开后,再按下x 这里字母是小写!(不要按着大写键)

  2. asp.net core 3.1 自定义中间件实现jwt token认证

    asp.net core 3.1 自定义中间件实现jwt token认证 话不多讲,也不知道咋讲!直接上代码 认证信息承载对象[user] /// <summary> /// 认证用户信息 ...

  3. SpringBoot之多模块项目

    SpringBoot之多模块项目 说明:我们通过maven的父子工程来搭建springboot的多模块项目** 项目的整体结构 本项目涉及了到了五个模块 framework-web模块主要是放置前端的 ...

  4. Linux恢复删除后数据文件

    简介 在使用Linux系统时,有时候会不小心误删除数据,由于Linux系统也没有与Windows系统下回收站类似的功能,一般会认为该文件将无法找回. 本文主要以CentOS7操作系统为例,介绍如何使用 ...

  5. Chrome简洁高效管理下载项

    每次要查看下载记录时,Chrome新建下载标签页的速度能让我抓狂 之前用的 Chrono 界面太过传统,而且还不能点击打开.所以一直在寻找好用的下载管理插件,经过多次在拓展商城搜索比对,找到了这款拓展 ...

  6. 微信小程序--家庭记账小账本(四)

    今天的进展不太顺利,总的账单表,代码改了又改,最后决定用一个新的表,账单界面中弄了一天删除,发现都无法实现想要的效果,于是把账单界面的删除功能去了,就感觉大功告成的时候,发现收入和支出界面的删除也出现 ...

  7. 新司机的致胜法宝,使用ApexSql Log2018快速恢复数据库被删除的数据

    作为开发人员,误操作数据delete.update.insert是最正常不过的了,比如: 删除忘记加where条件: 查询为了图方便按了F5,但是数据里面夹杂着delete语句. 不管是打着后发动机声 ...

  8. Java实现获取一个随机的两位数

    import java.util.Random; //获取一个随机的 两位数public class getrandomdouble { public static void main(String[ ...

  9. vue实现自定义表格列

    在我们开发PC端的项目使用表单时,尤其是crm系统,应该经常会遇到这样的需求, 用户需要根据设置来自定义显示列. 查了element的官方文档, 并没有此类组件, 所以手动封装了一个简单的组件, 希望 ...

  10. dom4j基本操作

    DOM4J与利用DOM.SAX.JAXP机制来解析xml相比,DOM4J 表现更优秀,具有性能优异.功能强大和极端易用使用的特点,只要懂得DOM基本概念,就可以通过dom4j的api文档来解析xml. ...