java图形界面多线程_Java自学-图形界面 Swing中的线程

本文详细介绍了Java Swing中的线程处理,包括初始化线程、事件调度线程和长耗时任务线程。强调了事件调度线程的单线程特性,以及如何通过SwingUtilities.invokeLater和SwingWorker来确保线程安全,避免界面阻塞。
摘要由CSDN通过智能技术生成

Swing中的线程

步骤 1 : 三种线程

在Swing程序的开发中,需要建立3种线程的概念

初始化线程

初始化线程用于创建各种容器,组件并显示他们,一旦创建并显示,初始化线程的任务就结束了。

事件调度线程

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

长耗时任务线程

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

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

9fcfdeccd027ef7abb3cbb366dc4c053.png

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

be2a59ddbbbedb7c31a4af57fdfbde2e.png

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 worker = new SwingWorker() {

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

}

}

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

6d5d2016f6fcde811c114fb7818c0c44.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值