Swing 的任务线程与 EDT 事件分发队列模型(下)

简介: Swing 的任务线程与 EDT 事件分发队列模型(下)

6 Swing 事件分发线程(EDT)

Swing的事件队列就类似事件队列,仅单一消费者,即一个事件分发线程。

除非你的程序停止,否则EDT会永不间断地徘徊在处理请求与等待请求之间。

Swing事件队列的实现机制图解

image.png

6.1 单一线程的事件队列的特性

  • 将同步操作转为异步操作
  • 将并行处理转换为串行顺序处理

6.2 EDT要处理所有GUI操作

  • 职责明确,任何GUI请求都应该在EDT中调用
  • 要处理的GUI请求非常多,包括窗口移动、组件自动重绘、刷新,它很忙。任何与GUI无关的处理不要由EDT执行,尤其是I/O耗时操作

7 Swing不是一个“安全线程”的API,为什么要这样设计

Swing的线程安全不是靠自身组件的API来保障,虽然repaint方法是这样,但是大多数SwingAPI是非线程安全的,也就是说不能在任意地方调用,它应该只在EDT中调用。Swing的线程安全靠事件队列和EDT保证。

8 invoke系方法

对非EDT的并发调用需通过invokeLater()和invokeAndWait()使请求插入到队列中等待EDT去执行。

由于Swing本身非线程安全,如果你在其他线程访问和修改GUI组件,必须使用

8.1 SwingUtilities. invokeAndWait(runnable)

image.png

同步的,它被调用结束会立即阻塞当前线程,直到EDT处理完该请求。

一般用于取得Swing组件的数据。

8.2 SwingUtilities. invokeLater(runnable)

使 doRun.run() 在AWT事件分法线程上异步执行。所有待处理的AWT事件被执行后,就会发生这种情况。当应用程序线程需要更新GUI时,应使用此方法。


在下面的示例中,invokeLater调用将Runnable对象doHelloWorld排队在事件分配线程上,然后打印一条消息。

 Runnable doHelloWorld = new Runnable() {
     public void run() {
         System.out.println("Hello World on " + Thread.currentThread());
     }
 };
 SwingUtilities.invokeLater(doHelloWorld);
 System.out.println("This might well be displayed before the other message.");

如果从事件分发线程(例如,从JButton的ActionListener)调用invokeLater,则 doRun.run 仍将延迟,直到处理完所有未决事件。请注意,如果doRun.run 引发未捕获的异常,则事件分发线程将展开(而不是当前线程)。


从1.3版本开始,此方法只是java.awt.EventQueue.invokeLater()的封面。


与Swing的其余部分不同,可以从任何线程调用此方法。


准则

不能在EDT中被调用,否则程序会抛出Error,请求也不会去执行。看源码:

 public static void invokeAndWait(Runnable runnable)
             throws InterruptedException, InvocationTargetException {
        //不能在EDT中调用invokeAndWait
        if (EventQueue.isDispatchThread()) {
            throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
        }
  class AWTInvocationLock {}
        Object lock = new AWTInvocationLock();
        InvocationEvent event = 
            new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
        true);
        synchronized (lock) {
            //添加进事件队列
            Toolkit.getEventQueue().postEvent(event);
            //block当前线程
            lock.wait();
        }
        Throwable eventThrowable = event.getThrowable();
        if (eventThrowable != null) {
            throw new InvocationTargetException(eventThrowable);
        }
    }

如果invokeAndWait在EDT中调用,那么首先将请求压进队列,然后EDT便被block,等待请求结束通知它继续运行。


而实际上请求将永远得不到执行,因为它在等待队列的调度使EDT执行它,这就陷入一个僵局:EDT等待请求先执行,请求又等待EDT对队列的调度。彼此等待对方释放锁是造成死锁的四类条件之一。Swing有意地避免了这类情况的发生。


目录
相关文章
|
17天前
|
存储 监控 Java
|
17天前
|
安全 Java 开发者
Swing 的线程安全分析
【8月更文挑战第22天】
28 4
|
16天前
|
安全 Java API
|
8天前
|
前端开发 JavaScript 大数据
React与Web Workers:开启前端多线程时代的钥匙——深入探索计算密集型任务的优化策略与最佳实践
【8月更文挑战第31天】随着Web应用复杂性的提升,单线程JavaScript已难以胜任高计算量任务。Web Workers通过多线程编程解决了这一问题,使耗时任务独立运行而不阻塞主线程。结合React的组件化与虚拟DOM优势,可将大数据处理等任务交由Web Workers完成,确保UI流畅。最佳实践包括定义清晰接口、加强错误处理及合理评估任务特性。这一结合不仅提升了用户体验,更为前端开发带来多线程时代的全新可能。
11 0
|
11天前
|
数据采集 Java Python
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
|
18天前
|
设计模式 安全 前端开发
Swing 是线程安全的吗?
【8月更文挑战第21天】
26 0
|
24天前
|
Cloud Native Java 调度
项目环境测试问题之线程同步器会造成执行完任务的worker等待的情况如何解决
项目环境测试问题之线程同步器会造成执行完任务的worker等待的情况如何解决
|
12天前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
38 1
|
4天前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。