理论解释见官方的文档:

https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html

一个Swing程序中一般有下面三种类型的线程:

  • 初始化线程(Initial Thread)
  • UI事件调度线程(EDT)
  • 任务线程(Worker Thread),可以看成后台其他的线程

每个程序必须有一个main方法,这是程序的入口。该方法运行在初始化或启动线程上。初始化线程读取程序参数并初始化一些对象。在许多Swing程序中,该线程主要目的是启动程序的图形用户界面(GUI)。一旦GUI启动后,对于大多数事件驱动的桌面程序来说,初始化线程的工作就结束了。
        Swing程序只有一个EDT线程,该线程负责GUI组件的绘制和更新,通过调用程序的事件处理器来响应用户交互。所有事件处理都是在EDT上进行的,程序同UI组件和其基本数据模型的交互只允许在EDT上进行,所有运行在EDT上的任务应该尽快完成,以便UI能及时响应用户输入。
        Swing编程时应该注意以下两点:
1.从其他线程访问UI组件及其事件处理器会导致界面更新和绘制错误。
2.在EDT上执行耗时任务会使程序失去响应,这会使GUI事件阻塞在队列中得不到处理。
3.应使用独立的任务线程来执行耗时计算或输入输出密集型任务,比如同数据库通信、访问网站资源、读写大树据量的文件。

  从java6开始,SwingWorker类帮你管理任务线程和Swing EDT之间的交互,对于任务线程来说,就是SwingWorker执行和界面无直接关系的耗时任务和I/O密集型操作。

一个主界面启动的正确姿势:

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
public void run() {
MainFrame mainUI = new MainFrame();
mainUI.showJFrame();
}
}); }

  将任务放到EDT执行的方法是SwingUtilities.invokeAndWait,不像invokeLater,invokeAndWait方法是阻塞执行的,它在EDT上执行Runnnable任务,直到任务执行完了,该方法才返回调用线程。
  invokeLater和invokeAndWait都在事件派发队列中的所有事件都处理完之后才执行它们的Runnable任务,也就是说,这两个方法将Runnable任务放在事件队列的末尾。
  注意:虽然可以在其他线程上调用invokeLater,也可以在EDT上调用invokeLater,但是千万不要在EDT线程上调用invokeAndWait方法!这样做会造成线程竞争,程序就会陷入死锁。那么一个好的办法是不要使用invokeAndWait方法。

一个演示阻塞和非阻塞的例子:

Factorial,用于普通阶乘计算类

package concurrency;

public class Factorial {

    private int n;

    public Factorial(int n) {
this.n = n;
} public Integer call() {
int result = 1; for (int i = 1; i <= n; i++) {
result = result * i;
} try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
ex.printStackTrace();
} System.out.println("currentThread = " + Thread.currentThread().getName());
return result;
} }

一个Callable的阶乘实现类

package concurrency;

import java.util.concurrent.Callable;

public class FactorialCalculator implements Callable<Integer> {
private int n; public FactorialCalculator(int n) {
this.n = n;
} public Integer call() {
int result = 1; for (int i = 1; i <= n; i++) {
result = result * i;
} try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
ex.printStackTrace();
} System.out.println("currentThread = " + Thread.currentThread().getName());
return result;
}
}

测试类

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask; import javax.swing.JButton;
import javax.swing.SwingWorker; import concurrency.Factorial;
import concurrency.FactorialCalculator; public class DoBackground { public DoBackground() {
// TODO Auto-generated constructor stub
} //线程池处理,阻塞主UI
public static void blockFutureGet() {
ExecutorService pool = Executors.newSingleThreadExecutor();
Future<Integer> factorialResult = pool.submit(new FactorialCalculator(8));
try {
// Integer factorialValue = factorialResult.get(3,
// TimeUnit.SECONDS);
Integer factorialValue = factorialResult.get();
System.out.println("Factorial Value (用FutureGet处理) = " + factorialValue); } catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} pool.shutdown();
} //线程池futureTask处理,演示组塞主UI
public static void blockFutureTaskGet() {
ExecutorService pool = Executors.newSingleThreadExecutor(); FactorialCalculator task = new FactorialCalculator(8);
FutureTask<Integer> futureTask = new FutureTask<Integer>(task); pool.submit(futureTask);
pool.shutdown();
try {
Integer factorialValue = futureTask.get();
System.out.println("Factorial Value (用FutureTaskGet处理) = " + factorialValue); } catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} } //用主线程处理,阻塞
public static void blockMainThread() {
Factorial cal = new Factorial(8);
System.out.println("Factorial Value (用主线程处理) = " + cal.call());
} //用FutureGet + swingwork处理
public static void swingworkConcurrency() {
final SwingWorker worker = new SwingWorker() { @Override
protected Object doInBackground() throws Exception {
// TODO Auto-generated method stub
ExecutorService pool = Executors.newSingleThreadExecutor();
Future<Integer> factorialResult = pool.submit(new FactorialCalculator(8));
try {
// Integer factorialValue = factorialResult.get(3,
// TimeUnit.SECONDS);
Integer factorialValue = factorialResult.get();
System.out.println("Factorial Value (用FutureGet + swingwork处理) = " + factorialValue); } catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} pool.shutdown();
return null;
} };
worker.execute();
} //用swingwork线程处理
public static void swingworkMainThread() {
final SwingWorker worker = new SwingWorker() { @Override
protected Object doInBackground() throws Exception {
try {
Factorial cal = new Factorial(8);
System.out.println("Factorial Value (用swingwork线程处理) = " + cal.call()); } catch (Exception ex) {
ex.printStackTrace();
}
return null;
} };
worker.execute();
} /**
* 用swingwork分发线程处理回调,并读写外部的变量值
* @param jbtn,SwingWorker外部的对象
*/
public static void swingworkCallback(JButton jbtn) { Integer backInteger; SwingWorker<Integer, Object> worker = new SwingWorker<Integer, Object>() { @Override
protected Integer doInBackground() throws Exception {
try {
Factorial cal = new Factorial(8); return cal.call();
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
} @Override
public void done() {
try {
System.out.println("Factorial Value (用swingwork分发线程处理回调) = " + get());
//backInteger = (Integer) get();
setTitle(jbtn,get().toString());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} };
worker.execute();
} static void setTitle(JButton jbtn,String text){ jbtn.setText(text);
} }

结论:用后两种方法就已经能够实现非阻塞的设计了,即没必要自己再新建线程,或开线程池,请把一切交给swingwork去处理。

最后一个例子演示了回调如何和swing的其他对象(比如说一个jbutton进行数据读写,这是跨线程的操作,swingwork也支持的非常好)。

运行结果:

Swing中的线程并发处理的更多相关文章

  1. Java自学-图形界面 Swing中的线程

    Swing中的线程 步骤 1 : 三种线程 在Swing程序的开发中,需要建立3种线程的概念 初始化线程 初始化线程用于创建各种容器,组件并显示他们,一旦创建并显示,初始化线程的任务就结束了. 事件调 ...

  2. swing中的线程

    1. 初始化线程 初始化线程用于创建各种容器,组件并显示他们,一旦创建并显示,初始化线程的任务就结束了. 2. 事件调度线程(单线程:只有一个线程在负责事件的响应工作.) 通过事件监听的学习,我们了解 ...

  3. 【Swing】理解Swing中的事件与线程

    talk is cheap , show me the code. Swing中的事件 事件驱动 所有的GUI程序都是事件驱动的.Swing当然也是. GUI程序不同于Command Line程序,一 ...

  4. 深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) [转载]

    本系列文章导航 深入浅出Java多线程(1)-方法 join 深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) 深入浅出多线程(3)-Future异步模式以及在JDK1.5Concu ...

  5. 【转】Swing 与EDT线程

    在Swing程序中,经常能看到如下这种代码: SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { text ...

  6. 【代码总结● Swing中的一些操作与设置】

    Swing中设置关闭窗口验证与添加背景图片 package com.swing.test; import java.awt.EventQueue; import java.awt.Image; imp ...

  7. Stream入门及Stream在JVM中的线程表现

    继上次学习过Java8中的非常重要的Lambda表达式之后,接下来就要学习另一个也比较重要的知识啦,也就如标题所示:Stream,而它的学习是完全依赖于之前学习的Lambda表达式. 小实验引入: 这 ...

  8. android中的线程池学习笔记

    阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...

  9. rxjava源码中的线程知识

    rxjava源码中的线程知识 rx的最精简的总结就是:异步 这里说一下以下的五个类 1.Future2.ConcurrentLinkedQueue3.volatile关键字4.AtomicRefere ...

随机推荐

  1. java学习笔记(七):for循环

    java的for循环和c++的for循环类似 public class Test { public static void main(String args[]) { for(int x = 10; ...

  2. 解题(GeLeiMa -生成格雷码)

    题目描述 在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同, 则称这种编码为格雷码(Gray Code),请编写一个函数,使用递归的方法生成N位的格雷码. 给定一个整数n,请返回n位的格雷码 ...

  3. day40 mysql数据类型

    复习 1.环境的搭建 2.启动服务 3.库,表,字段的基本操作 create show drop alter desc insert into select from update set delet ...

  4. 简单了解pytorch的forward

    import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torch.autogra ...

  5. KBEngine 编译出现 MSB802 无法找到v140的生成工具

    我用的vs版本是vs2017professional版本,并未安装所有的工具 在编译kbengine源码时候出现 MSB802 无法找到v140的生成工具错误 修复办法在菜单栏选择  工具--> ...

  6. 35 【kubernetes】configMap

    kubernetes可以驱动容器的运行,并且把容器的运行放置在kubernetes定义的体系结构中pods这一级. 但是容器运行通常会需要某些参数,比如环境变量或者硬件使用情况. 为了解决对每个con ...

  7. 725. Split Linked List in Parts把链表分成长度不超过1的若干部分

    [抄题]: Given a (singly) linked list with head node root, write a function to split the linked list in ...

  8. mybatis-plus 自动生成代码

    public class MpGenerator { /** * <p> * MySQL 生成演示 * </p> */ public static void main(Stri ...

  9. linux ">/dev/null 2>&1 &"

    0:表示键盘输入(stdin)1:表示标准输出(stdout),系统默认是1 2:表示错误输出(stderr) command >/dev/null 2>&1 &  == ...

  10. bittorrent 学习(一) 种子文件分析与bitmap位图

    终于抽出时间来进行 BITTORRENT的学习了 BT想必大家都很熟悉了,是一种文件分发协议.每个下载者在下载的同时也在向其他下载者分享文件. 相对于FTP HTTP协议,BT并不是从某一个或者几个指 ...