步骤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 );
}
}
|
更多内容,点击了解: https://how2j.cn/k/gui/gui-thread/708.html