1. Swing界面假死
Swing界面编程采用事件驱动的方式,Swing线程(即事件驱动线程)负责所有组件的绘制、事件响应等等;整个界面都利用Swing线程来处理,因此不能并发执行,如对于一个按钮的事件,Swing就必须等待它处理完成才能返回继续处理其它工作,如继续响应按钮的点击、窗口的放大缩小等事件;这样如果在响应的事件中等待的时间过长,就会导致界面出现假死的现象。
举个例子:
package com.liuqi.learn.swing.thread;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.Thread.State;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import net.miginfocom.swing.MigLayout;
public class FryTest extends JFrame implements ActionListener{
private JButton startButton = new JButton("Start");
public FryTest(){
this.setTitle("Fry test");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
Container c = this.getContentPane();
c.setLayout(new MigLayout("insets 10", "[][]", "[][grow]"));
this.add(startButton, "w 100!");
pack();
startButton.addActionListener(this);
}
public static void main(String[] args){
FryTest test = new FryTest();
test.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(startButton)){
try {
Thread.sleep(4000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}
每次点击按钮后,按钮状态就变成这样了:
在按钮处理它的事件的时间内(即4秒内),其它事件只能等待,四秒完成后按钮状态恢复正常。
Swing此时的状态就如同一个仅有一个人管理的餐厅:又要在前台收钱,又要在厨房炒菜;这样当有一个顾客订餐的时候,他就跑到厨房去做菜,再来顾客时就只能呆呆的在前台等着他炒完菜后回来。
那怎么解决这个问题?既然一个人太忙了,就只能再招一个人了,一人在前台收钱,一人在后台炒菜。于是就需要使用到其它线程了:
2. 引入Thread
在按钮的事件处理中,引入一个Thread来处理其事情,这样Swing线程就可以很快返回到主界面,而将耗时操作放在其它线程中在后台执行。修改后的事件处理如下所示:
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(startButton)){
new Thread(){
public void run(){
try {
Thread.sleep(4000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}.start();
}
}
这次点击按钮,按钮再也不会像刚刚那样一直要四秒后才能恢复正常了,而是立即正常。
这里就使用到了事件处理线程之外的线程来完成具体的工作;但需要注意的是,当事件处理中需要对界面进行更新时,就需要使用另外的方式来使用多线程了。Java相关文档中指出,当组件被显示到界面上之后,就应该只有事件处理线程对它的外观进行改变,否则随时可能引起死锁!
因为使用的这些方法有很多是非线程安全的,同时使用工作线程和事件处理线程来进行处理,就有可能引起死锁。
当然也有例外,有些方法 如repaint、JTextComponent的setText方法等,是线程安全的,可以有多个线程对其进行访问。但在不确定的时候,最好是使用invokeAndWait或者是invokeLater来进行处理。
3. invokeAndWait与invokeLater
invokeAndWait会将一直等到事件处理返回,而invokeLater会将一个事件加入到队列后立即返回,而不管这个事件在什么时候进行处理。
这两个方法都会使得执行事件中的处理异步的在事件处理线程中进行,因此可以安全地对界面组件进行控制而不会引起死锁。
但需要注意的是如果从事件处理线程中调用invokeAndWait,也会引起事件处理线程一直在等待而出现界面死锁的现象。
但代码中过多的invokeAndWait及invokeLater会使得对代码的控制变得困难;于是新的方法又被引入,即SwingWorker
4. SwingWorker顾名思义,即是一个Worker,它主要负责将一些繁复的工作放到后台去执行,而在这个执行的过程中又可以将中间结果以一种安全的方式在界面上显示出来,从而不会引起死锁。用它来进行编程的框架如下所示 :
public class LoadWorker extends SwingWorker<Void, String> {
public LoadWorker(ContentTextPane textPane, File file){
}
@Override
protected Void doInBackground() throws Exception {
publish("test");
return null;
}
protected void process(List<String> list){
}
@Override
protected void done(){
}
}
主要有四个方法:
doInBackGround:在后台执行的耗时操作,其返回结果类型是SwingWorker<..,...>中的第一个参数类型
publish(...):在doInBackGround工作中将中间结果发送给Process进行处理
process(List<...> list):接受publish发送过来的数据,并更新界面显示。
done():线程完成时所做的工作
注意在doInBackground方法中执行的工作,是在非事件处理线程中执行的,因此不能在其中更新界面组件;如果需要更新界面组件,则可以使用publish返回数据,然后在process中进行处理——process中的操作是在事件处理线程完成的,因此可以安全的访问控制组件。
一个例子:
package com.liuqi.logtools.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Calendar;
import java.util.List;
import javax.swing.SwingWorker;
import javax.swing.text.BadLocationException;
import com.liuqi.logtools.ui.ContentTextPane;
public class CopyOfLoadWorker extends SwingWorker<Void, String> {
private ContentTextPane textPane;
private File file;
private FilterCache cache;
private Calendar startCalendar;
public static String WORK_THREAD_END = "WORKTHREADEND";
private String debugStr = LogLevel.DEBUG.toString();
private String errorStr = LogLevel.ERROR.toString();
private String warnStr = LogLevel.WARN.toString();
private String infoStr = LogLevel.INFO.toString();
public CopyOfLoadWorker(ContentTextPane textPane, File file) {
this.textPane = textPane;
this.file = file;
try {
textPane.getDocument()
.remove(0, textPane.getDocument().getLength());
} catch (BadLocationException e) {
e.printStackTrace();
}
cache = textPane.getFilterCache();
}
@Override
protected Void doInBackground() throws Exception {
startCalendar = Calendar.getInstance();
// 先清空显示
BufferedReader reader = null;
reader = new BufferedReader(new FileReader(file));
String line = "";
while ((line = reader.readLine()) != null) {
if (!cache.isSatisfy(line)) {
continue;
}
publish(line);
}
reader.close();
return null;
}
protected void process(List<String> list) {
for (String line : list) {
String str = "";
if (line.length() > 130) {
str = line.substring(0, 130);
} else if (line.indexOf(0) == '[') {
str = line.substring(0, 8);
} else {
str = line;
}
if (str.contains(debugStr)) {
textPane.debug(line);
} else if (str.contains(infoStr)) {
textPane.info(line);
} else if (str.contains(errorStr)) {
textPane.error(line);
} else {
textPane.warn(line);
}
}
}
@Override
protected void done() {
if (null != startCalendar) {
System.out.println("Total time: "
+ (Calendar.getInstance().getTimeInMillis() - startCalendar
.getTimeInMillis()));
}
}
}
在该例子中,由doInBackground方法中读取一个文件,对其中的行进行判断其是否满足一定的条件,然后再使用publish传递到process方法中,由process来将其显示在textPane上。在工作完成之后,计算其所消耗的时间。
- 大小: 10.7 KB
分享到:
相关推荐
【Swing线程的最后讨论 -- 利用异步模型】 Swing框架遵循单线程规则,即所有UI组件在同一时间只能被一个线程访问,通常这个线程就是事件派发线程。这一规则是为了避免多线程环境下的竞态条件和同步问题,确保组件的...
【基于Swing的多线程聊天室】是一个...总结,基于Swing的多线程聊天室是一个综合运用了GUI设计、多线程、网络编程以及数据处理等技术的项目。通过这种方式,可以创建出一个高效、响应迅速并且用户体验良好的聊天应用。
### Java开发中的线程安全选择与Swing 在Java开发中,Swing作为构建桌面应用程序图形用户界面(GUI)的主要工具之一,其设计目标是为了提供一个强大、灵活且易于使用的框架。Swing允许开发者轻松创建自定义组件或...
Swing 线程的深入理解和 SwingWorker 基础知识介绍 Swing 是 Java 中的一种图形用户界面(GUI)工具包,用于创建桌面应用程序。然而,在使用 Swing 时,开发者经常会遇到线程相关的问题。本文将深入了解 Swing 线程...
本项目应用总结主要聚焦于Swing组件的使用、事件处理以及自定义组件的实现。博客链接提供了更深入的探讨。 1. **Swing组件库**:Swing提供了丰富的组件集,如JButton、JLabel、JFrame、JPanel、JTextArea、JTable等...
总结起来,Java+Swing即时聊天系统是通过结合Java的Swing库构建用户界面,利用多线程进行并发处理,通过Socket进行网络通信,实现用户间的实时消息传递。这样的系统为学习和理解Java的GUI编程、多线程以及网络编程...
总结来说,这个程序展示了如何使用Swing的`JFrame`和`JLabel`组件来创建一个简单的窗口应用,并结合多线程(通过`Timer`类)实现时间的实时显示。同时,遵循了Swing的线程安全原则,确保了UI操作的正确性。这是一个...
总结起来,"Java Swing 赛马小游戏 线程实例"涵盖了以下关键知识点: 1. Java Swing组件的使用,包括布局管理、事件监听和用户交互。 2. Java多线程的概念,如何创建和控制线程。 3. 线程同步和通信,如...
总结来说,"java多线程文件传输(基于swing)"项目展示了如何利用Java的Socket编程和多线程技术实现高效、可靠的文件传输,同时结合Swing提供用户友好的交互体验。这个项目对于学习网络编程、多线程以及GUI设计的...
### Java局域网聊天应用:Swing、Socket与线程技术详解 #### 一、项目概述 本项目是一个基于Java的局域网聊天工具,它使用了Swing框架来构建图形用户界面(GUI),并利用Socket编程实现了客户端与服务器之间的通信...
总结来说,这个项目展示了如何利用Java Swing的`Timer`类在单线程环境下构建一个功能完整的贪吃蛇游戏,同时体现了Swing组件绘制、事件处理和资源管理的能力。这样的实现方式简化了编程复杂性,但可能限制了游戏的...
这是程序的入口类,通过调用 `EventQueue.invokeLater` 方法来确保所有Swing组件在事件调度线程中初始化和显示。创建了一个 `AnimationFrame` 类的实例,并将其设置为可见。 ##### 2. `AnimationFrame` 类 - **功能...
总结来说,这个实验旨在帮助开发者深入理解Java多线程的概念,熟练运用`Thread`类的`run`和`start`方法,以及如何通过进度条来实时展示多线程任务的执行进度。通过实践,开发者可以更好地掌握并发编程的技巧,这对于...
总结起来,"一个简单Swing窗体"项目展示了如何使用Java Swing创建一个具有交互功能的窗口应用,包括创建JFrame、添加组件、处理事件以及维护点击计数。Swing的组件库、事件处理机制、布局管理和线程安全特性共同构建...
Java Swing版QQ是一款使用Java编程语言的...总结来说,Java Swing版QQ项目涵盖了Java GUI设计、网络编程、多线程并发、服务器管理、数据传输与序列化等多个核心编程概念,对于学习和提升Java全栈开发能力非常有帮助。
总结来说,理解并掌握线程的挂起、唤醒和终止是多线程编程中的基础技能。合理运用这些操作,可以实现更高效、更健壮的并发程序。在实际开发中,我们需要遵循最佳实践,避免使用已过时的线程控制方法,而是采用更安全...
总结来说,Java编写线程动画涉及的核心知识点包括:进程与线程的概念、Java中线程的创建方式、Swing和JavaFX等GUI库的使用、线程安全以及动画原理。理解并熟练掌握这些知识点,能帮助开发者构建出流畅、高效的线程...
总结来说,"swing-学生选课系统"是一个集成了Swing界面设计、JDBC数据库操作、多线程处理和用户交互的综合性项目。通过学习和实践这个系统,开发者可以深入理解Swing在实际项目中的应用,以及如何与数据库进行高效...
总结来说,实现Java Swing的计时与倒计时功能涉及到以下几个关键点: 1. 使用Swing组件创建用户界面,如JFrame、JTextField、JButton等。 2. 处理用户输入的时间,确保格式正确。 3. 使用多线程或者javax.swing....