`
lwp2000
  • 浏览: 74764 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

java解决swing单线程卡死

    博客分类:
  • java
 
阅读更多
现在我们要做一个简单的界面。
包括一个进度条、一个输入框、开始和停止按钮。
需要实现的功能是:
当点击开始按钮,则更新进度条,并且在输入框内把完成的百分比输出(这里只做例子,没有真正去做某个工作)。

代码1:
view plaincopy to clipboardprint?
1. import java.awt.FlowLayout; 
2. import java.awt.event.ActionEvent; 
3. import java.awt.event.ActionListener; 
4. import javax.swing.JButton; 
5. import javax.swing.JFrame; 
6. import javax.swing.JProgressBar; 
7. import javax.swing.JTextField; 
8. public class SwingThreadTest1 extends JFrame { 
9.        private static final long serialVersionUID = 1L; 
10.        private static final String STR = "Completed : "; 
11.        private JProgressBar progressBar = new JProgressBar(); 
12.        private JTextField text = new JTextField(10); 
13.        private JButton start = new JButton("Start"); 
14.        private JButton end = new JButton("End"); 
15.        private boolean flag = false; 
16.        private int count = 0; 
17.        public SwingThreadTest1() { 
18.          this.setLayout(new FlowLayout()); 
19.          add(progressBar); 
20.          text.setEditable(false); 
21.          add(text); 
22.          add(start); 
23.          add(end); 
24.          start.addActionListener(new Start()); 
25.          end.addActionListener(new End()); 
26.        } 
27.            
28.        private void go() { 
29.          while (count < 100) { 
30.             try { 
31.                    Thread.sleep(100);//这里比作要完成的某个耗时的工作 
32.             } catch (InterruptedException e) { 
33.                    e.printStackTrace(); 
34.             } 
35.                             //更新进度条和输入框 
36.             if (flag) { 
37.                    count++; 
38.                    progressBar.setValue(count); 
39.                    text.setText(STR + String.valueOf(count) + "%"); 
40.             } 
41.          } 
42.        } 
43.        private class Start implements ActionListener { 
44.          public void actionPerformed(ActionEvent e) { 
45.             flag = true;//设置开始更新的标志 
46.             go();//开始工作 
47.          } 
48.        } 
49.        private class End implements ActionListener { 
50.          public void actionPerformed(ActionEvent e) { 
51.             flag = false;//停止 
52.          } 
53.        } 
54.        public static void main(String[] args) { 
55.          SwingThreadTest1 fg = new SwingThreadTest1(); 
56.          fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
57.          fg.setSize(300, 100); 
58.          fg.setVisible(true); 
59.        } 
60. } 

运行代码发现,
现象1:当点击了开始按钮,画面就卡住了。按钮不能点击,进度条没有被更新,输入框上也没有任何信息。
原因分析:Swing是线程不安全的,是单线程的设计,所以只能从事件派发线程访问将要在屏幕上绘制的Swing组件。ActionListener的actionPerformed方法是在事件派发线程中调用执行的,而点击了开始按钮后,执行了go()方法,在go()里,虽然也去执行了更新组件的方法
progressBar.setValue(count);
text.setText(STR + String.valueOf(count) + "%");
但由于go()方法直到循环结束,它并没有返回,所以更新组件的操作一直没有被执行,这就造成了画面卡住的现象。
现象2:过了一段时间(go方法里的循环结束了)后,画面又可以操作,并且进度条被更新,输入框也出现了我们想看到的信息。
原因分析:通过在现象1的分析,很容易联想到,当go()方法返回了,则其他的线程(更新组件)可以被派发了,所以画面上的组件被更新了。
为了让画面不会卡住,我们来修改代码,将耗时的工作放在一个线程里去做。

代码2:
view plaincopy to clipboardprint?
1. import java.awt.FlowLayout; 
2. import java.awt.event.ActionEvent; 
3. import java.awt.event.ActionListener; 
4. import javax.swing.JButton; 
5. import javax.swing.JFrame; 
6. import javax.swing.JProgressBar; 
7. import javax.swing.JTextField; 
8. public class SwingThreadTest2 extends JFrame { 
9.        private static final long serialVersionUID = 1L; 
10.        private static final String STR = "Completed : "; 
11.        private JProgressBar progressBar = new JProgressBar(); 
12.        private JTextField text = new JTextField(10); 
13.        private JButton start = new JButton("Start"); 
14.        private JButton end = new JButton("End"); 
15.        private boolean flag = false; 
16.        private int count = 0; 
17.      
18.        GoThread t = null; 
19.        public SwingThreadTest2() { 
20.          this.setLayout(new FlowLayout()); 
21.          add(progressBar); 
22.          text.setEditable(false); 
23.          add(text); 
24.          add(start); 
25.          add(end); 
26.          start.addActionListener(new Start()); 
27.          end.addActionListener(new End()); 
28.        } 
29.        private void go() { 
30.          while (count < 100) { 
31.             try { 
32.                    Thread.sleep(100); 
33.             } catch (InterruptedException e) { 
34.                    e.printStackTrace(); 
35.             } 
36.             if (flag) { 
37.                    count++; 
38.                    System.out.println(count); 
39.                    progressBar.setValue(count); 
40.                    text.setText(STR + String.valueOf(count) + "%"); 
41.             } 
42.          } 
43.        } 
44.        private class Start implements ActionListener { 
45.          public void actionPerformed(ActionEvent e) { 
46.             flag = true; 
47.             if(t == null){ 
48.                    t = new GoThread(); 
49.                    t.start(); 
50.             } 
51.          } 
52.        } 
53.        //执行复杂工作,然后更新组件的线程 
54.        class GoThread extends Thread{ 
55.          public void run() { 
56.             //do something... 
57.             go(); 
58.          } 
59.        } 
60.        private class End implements ActionListener { 
61.          public void actionPerformed(ActionEvent e) { 
62.             flag = false; 
63.          } 
64.        } 
65.        public static void main(String[] args) { 
66.          SwingThreadTest2 fg = new SwingThreadTest2(); 
67.          fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
68.          fg.setSize(300, 100); 
69.          fg.setVisible(true); 
70.        } 
71. } 

我们执行了程序,结果和我们想要的一样,画面不会卡住了。
那这个程序是否没有问题了呢?
我们自定义了一个线程GoThread,在这里我们完成了那些耗时的工作,可以看作是“工作线程”,
而对于组件的更新,我们也放在了“工作线程”里完成了。
在这里,在事件派发线程以外的线程里设置进度条,是一个危险的操作,运行是不正常的。(对于输入框组件的更新是安全的。)

只有从事件派发线程才能更新组件,根据这个原则,我们来修改我们现有代码。
代码3:
view plaincopy to clipboardprint?
1. import java.awt.FlowLayout; 
2. import java.awt.event.ActionEvent; 
3. import java.awt.event.ActionListener; 
4. import javax.swing.JButton; 
5. import javax.swing.JFrame; 
6. import javax.swing.JProgressBar; 
7. import javax.swing.JTextField; 
8. import javax.swing.SwingUtilities; 
9. public class SwingThreadTest3 extends JFrame { 
10.        private static final long serialVersionUID = 1L; 
11.        private static final String STR = "Completed : "; 
12.        private JProgressBar progressBar = new JProgressBar(); 
13.        private JTextField text = new JTextField(10); 
14.        private JButton start = new JButton("Start"); 
15.        private JButton end = new JButton("End"); 
16.        private boolean flag = false; 
17.        private int count = 0; 
18.      
19.        private GoThread t = null; 
20.      
21.        private Runnable run = null;//更新组件的线程 
22.        public SwingThreadTest3() { 
23.          this.setLayout(new FlowLayout()); 
24.          add(progressBar); 
25.          text.setEditable(false); 
26.          add(text); 
27.          add(start); 
28.          add(end); 
29.          start.addActionListener(new Start()); 
30.          end.addActionListener(new End()); 
31.            
32.          run = new Runnable(){//实例化更新组件的线程 
33.             public void run() { 
34.                    progressBar.setValue(count); 
35.                    text.setText(STR + String.valueOf(count) + "%"); 
36.             } 
37.          }; 
38.        } 
39.        private void go() { 
40.          while (count < 100) { 
41.             try { 
42.                    Thread.sleep(100); 
43.             } catch (InterruptedException e) { 
44.                    e.printStackTrace(); 
45.             } 
46.             if (flag) { 
47.                    count++; 
48.                    SwingUtilities.invokeLater(run);//将对象排到事件派发线程的队列中 
49.             } 
50.          } 
51.        } 
52.        private class Start implements ActionListener { 
53.          public void actionPerformed(ActionEvent e) { 
54.             flag = true; 
55.             if(t == null){ 
56.                    t = new GoThread(); 
57.                    t.start(); 
58.             } 
59.          } 
60.        } 
61.      
62.        class GoThread extends Thread{ 
63.          public void run() { 
64.             //do something... 
65.             go(); 
66.          } 
67.        } 
68.        private class End implements ActionListener { 
69.          public void actionPerformed(ActionEvent e) { 
70.             flag = false; 
71.          } 
72.        } 
73.        public static void main(String[] args) { 
74.          SwingThreadTest3 fg = new SwingThreadTest3(); 
75.          fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
76.          fg.setSize(300, 100); 
77.          fg.setVisible(true); 
78.        } 
79. } 

解释:SwingUtilities.invokeLater()方法使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。
还有一个方法SwingUtilities.invokeAndWait()方法,它也可以使事件派发线程上的可运行对象排队。
他们的不同之处在于:SwingUtilities.invokeLater()在把可运行的对象放入队列后就返回,而SwingUtilities.invokeAndWait()一直等待知道已启动了可运行的run方法才返回。如果一个操作在另外一个操作执行之前必须从一个组件获得信息,则应使用SwingUtilities.invokeAndWait()方法
分享到:
评论

相关推荐

    Java Swing多线程死锁问题解析

    在解决Java Swing多线程死锁问题时,我们需要了解Java Swing的多线程机理和相关API的使用。SwingUtilities.invokeLater和SwingUtilities.isEventDispatchThread()是我们解决死锁问题的重要工具。 在实际开发中,...

    JAVASWING多线程产生随机球

    在“JAVASWING多线程产生随机球”的项目中,开发者利用Swing创建了一个互动的应用程序,用户可以通过鼠标点击在界面上生成一个球体,这个球体会以随机的方向和速度在窗口内移动。下面将详细解释这个项目涉及的知识点...

    java Swing 多线程下载器

    Java Swing多线程下载器是一种利用Java Swing库构建的图形用户界面(GUI)应用程序,它具备多线程下载功能,并支持断点续传。这样的工具类似于我们熟知的迅雷下载管理器,允许用户同时下载多个文件,提高下载速度,...

    JavaSwing多人猜拳

    尽管缺少源代码,我们可以推断出这个JavaSwing应用涉及到的技术和概念,包括图形用户界面设计、事件处理、多线程、网络编程(如果适用)、游戏逻辑实现以及可能的数据持久化。对于想要学习JavaSwing或者想了解如何...

    JAVA 开发 Swing与多线程

    Swing虽然是Java语言的一部分,但它设计为单线程模型,主要是为了简化GUI编程并避免复杂的同步问题。 Swing的单线程模型基于"事件派发线程"(Event Dispatch Thread, EDT),所有Swing组件的渲染和事件处理都在这个...

    基于java swing的多线程电梯调度模拟

    在本项目"基于Java Swing的多线程电梯调度模拟"中,我们主要探讨的是如何利用Java的多线程特性来实现一个复杂的系统——电梯调度。这个任务是在操作系统课程中的一个典型作业,它要求开发者模拟真实世界中的电梯运行...

    java+swing漂亮软件界面+超酷+以及javaswing教程.7z

    Java Swing 是Java平台上用于构建图形用户界面(GUI)的一个库,它是Java Foundation Classes (JFC)的一部分。在Java中,Swing提供了丰富的组件集,允许开发者创建美观且功能强大的桌面应用程序。"java+swing漂亮...

    基于Java,Swing的2048小游戏.zip

    基于Java,Swing的2048小游戏.zip基于Java,Swing的2048小游戏.zip基于Java,Swing的2048小游戏.zip基于Java,Swing的2048小游戏.zip基于Java,Swing的2048小游戏.zip基于Java,Swing的2048小游戏.zip基于Java,...

    java+swing聊天室

    总的来说,Java Swing聊天室项目结合了GUI设计、事件处理、网络编程和多线程技术,是一个全面展示Java桌面应用开发能力的好例子。通过学习和分析这样的项目,开发者不仅可以提升Swing应用的构建技巧,还能加深对Java...

    90个java-swing基础例子.zip

    10. **线程管理**:Swing不是线程安全的,所有对Swing组件的修改应在Event Dispatch Thread (EDT)中进行,以避免并发问题。 通过这90个基础例子,你可以逐步了解和掌握如何使用Swing创建组件,布局组件,处理用户...

    java swing漂亮界面 超酷 javaswing教程

    Java Swing是Java编程语言中的一个图形用户界面(GUI)工具包,它是Java Foundation Classes (JFC)的一部分,用于帮助开发者创建桌面应用程序。Swing提供了一系列组件,如按钮、文本框、菜单等,允许开发者构建功能...

    Java swing画随机圆

    Java Swing 是Java图形用户界面(GUI)工具包的一部分,它允许开发者创建丰富的桌面应用程序。在“Java Swing画随机圆”这个题目中,学生被要求利用Java Swing库来创建一个程序,该程序能够在屏幕上动态地绘制出大小...

    Java+Swing+Mysql实现酒店管理系统

    Java+Swing+MySQL实现的酒店管理系统是一款基于Java编程语言,采用Swing作为图形用户界面(GUI)开发工具,结合MySQL数据库存储数据的管理软件。这个系统为酒店提供了全面、高效且用户友好的操作界面,涵盖了酒店...

    Java+Swing+Mysql停车场管理系统

    【Java+Swing+Mysql停车场管理系统】是一个基于Java编程语言,使用Swing图形用户界面库,结合Mysql数据库技术实现的停车管理解决方案。这个系统的设计和开发旨在提供高效、安全且用户友好的车辆出入管理和车位管理...

    Java+Swing+Txt实现自助款机系统

    Java Swing 是Java GUI(图形用户界面)开发的一个重要框架,它是Java Foundation Classes (JFC) 的一部分,提供了丰富的组件和接口来创建桌面应用程序。在这个"Java+Swing+Txt实现自助款机系统"中,开发者利用Swing...

    java swing漂亮界面(超酷) javaswing教程

    Java Swing 是Java编程语言中用于构建桌面应用程序用户界面的一个库,它是Java Foundation Classes (JFC) 的一部分。Swing 提供了一系列组件,如按钮、文本框、菜单等,用于创建功能丰富的图形用户界面(GUI)。在...

    Java_swing_api_中文

    7. **Swing Worker**:Swing Worker类是用来处理后台任务的,它解决了Swing组件在主UI线程外执行耗时操作的问题,避免了用户界面的冻结。 8. ** Nimbus Look and Feel**:Nimbus是Swing自带的一种现代外观和感觉,...

    Java+Swing即时聊天系统,客户端和服务端,多线程,socket

    总结起来,Java+Swing即时聊天系统是通过结合Java的Swing库构建用户界面,利用多线程进行并发处理,通过Socket进行网络通信,实现用户间的实时消息传递。这样的系统为学习和理解Java的GUI编程、多线程以及网络编程...

    javaSwing皮肤大全.rar

    Java Swing 是Java GUI(图形用户界面)库的一部分,它提供了丰富的组件和工具,用于构建桌面应用程序。Swing 提供了一种可定制外观和感觉(LookAndFeel)的方法,使得开发者可以改变应用的视觉风格,以满足不同用户...

    Javaswing多线程.zip

    在这个“Javaswing多线程.zip”项目中,开发者创建了一个包含四个标签(JLabels)的窗口,这些标签会按照一定的时间间隔轮流显示或隐藏,这通常是为了创建某种动画效果或者信息滚动展示。在Swing中,我们通常使用`...

Global site tag (gtag.js) - Google Analytics