java swing 多线程_Swing多线程

本文介绍了如何在Java Swing中使用多线程避免界面卡死。通过示例代码展示了在不使用多线程时界面卡死的问题,然后改进代码,将耗时操作放在单独的线程中,最后使用`SwingUtilities.invokeLater()`确保组件更新在事件派发线程中执行,从而解决了问题。
摘要由CSDN通过智能技术生成

现在我们要做一个简单的界面。

包括一个进度条、一个输入框、开始和停止按钮。

需要实现的功能是:

当点击开始按钮,则更新进度条,并且在输入框内把完成的百分比输出(这里只做例子,没有真正去做某个工作)。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagetest;2

3 importjava.awt.FlowLayout;4 importjava.awt.event.ActionEvent;5 importjava.awt.event.ActionListener;6

7 importjavax.swing.JButton;8 importjavax.swing.JFrame;9 importjavax.swing.JProgressBar;10 importjavax.swing.JTextField;11

12 public class SwingThreadTest1 extendsJFrame {13 private static final long serialVersionUID = 1L;14 private static final String STR = "Completed : ";15 private JProgressBar progressBar = newJProgressBar();16 private JTextField text = new JTextField(10);17 private JButton start = new JButton("Start");18 private JButton end = new JButton("End");19 private boolean flag = false;20 private int count = 0;21

22 publicSwingThreadTest1() {23 this.setLayout(newFlowLayout());24 add(progressBar);25 text.setEditable(false);26 add(text);27 add(start);28 add(end);29 start.addActionListener(newStart());30 end.addActionListener(newEnd());31 }32

33 private voidgo() {34 while (count < 100) {35 try{36 Thread.sleep(100);//这里比作要完成的某个耗时的工作

37 } catch(InterruptedException e) {38 e.printStackTrace();39 }40 //更新进度条和输入框

41 if(flag) {42 count++;43 progressBar.setValue(count);44 text.setText(STR + String.valueOf(count) + "%");45 }46 }47 }48

49 private class Start implementsActionListener {50 public voidactionPerformed(ActionEvent e) {51 flag = true;//设置开始更新的标志

52 go();//开始工作

53 System.out.println(Thread.currentThread().getName());54 }55 }56

57 private class End implementsActionListener {58 public voidactionPerformed(ActionEvent e) {59 flag = false;//停止

60 }61 }62

63 public static voidmain(String[] args) {64 SwingThreadTest1 fg = newSwingThreadTest1();65 fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);66 fg.setSize(300, 100);67 fg.setVisible(true);68 }69 }

View Code

运行代码发现,

现象1:当点击了开始按钮,画面就卡住了。按钮不能点击,进度条没有被更新,输入框上也没有任何信息。

原因分析:Swing是线程不安全的,是单线程的设计,所以只能从事件派发线程访问将要在屏幕上绘制的Swing组件。ActionListener的actionPerformed方法是在事件派发线程中调用执行的,而点击了开始按钮后,执行了go()方法,在go()里,虽然也去执行了更新组件的方法

progressBar.setValue(count);

text.setText(STR + String.valueOf(count) + "%");

但由于go()方法直到循环结束,它并没有返回,所以更新组件的操作一直没有被执行,这就造成了画面卡住的现象。

go方法也一直在事件派发进程上,和更新UI在一个进程中,在一个进程中就必然是顺序执行的了。

现象2:过了一段时间(go方法里的循环结束了)后,画面又可以操作,并且进度条被更新,输入框也出现了我们想看到的信息。

原因分析:通过在现象1的分析,很容易联想到,当go()方法返回了,则其他的线程(更新组件)可以被派发了,所以画面上的组件被更新了。

为了让画面不会卡住,我们来修改代码,将耗时的工作放在一个线程里去做。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagetest;2

3 importjava.awt.FlowLayout;4 importjava.awt.event.ActionEvent;5 importjava.awt.event.ActionListener;6

7 importjavax.swing.JButton;8 importjavax.swing.JFrame;9 importjavax.swing.JProgressBar;10 importjavax.swing.JTextField;11

12 public class SwingThreadTest2 extendsJFrame {13 private static final long serialVersionUID = 1L;14 private static final String STR = "Completed : ";15 private JProgressBar progressBar = newJProgressBar();16 private JTextField text = new JTextField(10);17 private JButton start = new JButton("Start");18 private JButton end = new JButton("End");19 private boolean flag = false;20 private int count = 0;21

22 GoThread t = null;23

24 publicSwingThreadTest2() {25 this.setLayout(newFlowLayout());26 add(progressBar);27 text.setEditable(false);28 add(text);29 add(start);30 add(end);31 start.addActionListener(newStart());32 end.addActionListener(newEnd());33 }34

35 private voidgo() {36 while (count < 100) {37 try{38 Thread.sleep(10);39 } catch(InterruptedException e) {40 e.printStackTrace();41 }42 if(flag) {43 count++;44 System.out.println(count);45 progressBar.setValue(count);46 System.out.println(Thread.currentThread().getName());47 text.setText(STR + String.valueOf(count) + "%");48 }49 }50 }51

52 private class Start implementsActionListener {53 public voidactionPerformed(ActionEvent e) {54 System.out.println(Thread.currentThread().getName());55 flag = true;56 if (t == null) {57 t = newGoThread();58 t.start();59 }60 }61 }62

63 //执行复杂工作,然后更新组件的线程

64 class GoThread extendsThread {65 public voidrun() {66 //do something...

67 go();68 }69 }70

71 private class End implementsActionListener {72 public voidactionPerformed(ActionEvent e) {73 flag = false;74 }75 }76

77 public static voidmain(String[] args) {78 SwingThreadTest2 fg = newSwingThreadTest2();79 fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);80 fg.setSize(300, 100);81 fg.setVisible(true);82 }83 }

View Code

我们执行了程序,结果和我们想要的一样,画面不会卡住了。

那这个程序是否没有问题了呢?

我们自定义了一个线程GoThread,在这里我们完成了那些耗时的工作,可以看作是“工作线程”,

而对于组件的更新,我们也放在了“工作线程”里完成了。

在这里,在事件派发线程以外的线程里设置进度条,是一个危险的操作,运行是不正常的。(对于输入框组件的更新是安全的。)

只有从事件派发线程才能更新组件,根据这个原则,我们来修改我们现有代码。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagetest;2

3 importjava.awt.FlowLayout;4 importjava.awt.event.ActionEvent;5 importjava.awt.event.ActionListener;6

7 importjavax.swing.JButton;8 importjavax.swing.JFrame;9 importjavax.swing.JProgressBar;10 importjavax.swing.JTextField;11 importjavax.swing.SwingUtilities;12

13 public class SwingThreadTest3 extendsJFrame {14 private static final long serialVersionUID = 1L;15 private static final String STR = "Completed : ";16 private JProgressBar progressBar = newJProgressBar();17 private JTextField text = new JTextField(10);18 private JButton start = new JButton("Start");19 private JButton end = new JButton("End");20 private boolean flag = false;21 private int count = 0;22

23 private GoThread t = null;24

25 private Runnable run = null;//更新组件的线程

26

27 publicSwingThreadTest3() {28 this.setLayout(newFlowLayout());29 add(progressBar);30 text.setEditable(false);31 add(text);32 add(start);33 add(end);34 start.addActionListener(newStart());35 end.addActionListener(newEnd());36

37 run = new Runnable() {//实例化更新组件的线程

38 public voidrun() {39 System.out.println(Thread.currentThread().getName());40 progressBar.setValue(count);41 text.setText(STR + String.valueOf(count) + "%");42 }43 };44 }45

46 private voidgo() {47 while (count < 100) {48 try{49 Thread.sleep(10);50 } catch(InterruptedException e) {51 e.printStackTrace();52 }53 if(flag) {54 count++;55 System.out.println(Thread.currentThread().getName());56 SwingUtilities.invokeLater(run);//将对象排到事件派发线程的队列中

57 }58 }59 }60

61 private class Start implementsActionListener {62 public voidactionPerformed(ActionEvent e) {63 flag = true;64 if (t == null) {65 t = newGoThread();66 t.start();67 }68 }69 }70

71 class GoThread extendsThread {72 public voidrun() {73 //do something...

74 go();75 }76 }77

78 private class End implementsActionListener {79 public voidactionPerformed(ActionEvent e) {80 flag = false;81 }82 }83

84 public static voidmain(String[] args) {85 SwingThreadTest3 fg = newSwingThreadTest3();86 fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);87 fg.setSize(300, 100);88 fg.setVisible(true);89 }90 }

View Code

解释:SwingUtilities.invokeLater()方法使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。

还有一个方法SwingUtilities.invokeAndWait()方法,它也可以使事件派发线程上的可运行对象排队。

区别:

下面这个是弹出个alert窗口。若用invokeAndWait(),那么打印一段文字将在你点击了OK buton之后才会执行,而如用invokeLater()则,立马后执行输出操作。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagetest;2

3 importjava.lang.reflect.InvocationTargetException;4

5 importjavax.swing.JOptionPane;6 importjavax.swing.SwingUtilities;7

8 public classInvoke {9 public static voidmain(String[] args) {10 Runnable showModalDialog = newRunnable() {11 public voidrun() {12 JOptionPane.showMessageDialog(null, "No active shares found on this IP!");13 }14 };15 try{16 SwingUtilities.invokeAndWait(showModalDialog);17 System.out.println("sssssssssss");18 } catch(InterruptedException e) {19 //TODO Auto-generated catch block

20 e.printStackTrace();21 } catch(InvocationTargetException e) {22 //TODO Auto-generated catch block

23 e.printStackTrace();24 }25 }26 }

View Code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值