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有意地避免了这类情况的发生。


目录
相关文章
|
15天前
|
并行计算 JavaScript 前端开发
单线程模型
【10月更文挑战第15天】
|
9天前
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
25 7
|
9天前
|
消息中间件 存储 安全
|
1月前
SDL事件处理以及线程使用(2)
SDL库中事件处理和多线程编程的基本概念和示例代码,包括如何使用SDL事件循环来处理键盘和鼠标事件,以及如何创建和管理线程、互斥锁和条件变量。
25 1
SDL事件处理以及线程使用(2)
|
17天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
13 1
|
1月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
72 5
|
1月前
|
NoSQL Redis 数据库
Redis单线程模型 redis 为什么是单线程?为什么 redis 单线程效率还能那么高,速度还能特别快
本文解释了Redis为什么采用单线程模型,以及为什么Redis单线程模型的效率和速度依然可以非常高,主要原因包括Redis操作主要访问内存、核心操作简单、单线程避免了线程竞争开销,以及使用了IO多路复用机制epoll。
41 0
Redis单线程模型 redis 为什么是单线程?为什么 redis 单线程效率还能那么高,速度还能特别快
|
25天前
|
安全 调度 C#
STA模型、同步上下文和多线程、异步调度
【10月更文挑战第19天】本文介绍了 STA 模型、同步上下文和多线程、异步调度的概念及其优缺点。STA 模型适用于单线程环境,确保资源访问的顺序性;同步上下文和多线程提高了程序的并发性和响应性,但增加了复杂性;异步调度提升了程序的响应性和资源利用率,但也带来了编程复杂性和错误处理的挑战。选择合适的模型需根据具体应用场景和需求进行权衡。
|
1月前
|
消息中间件 NoSQL 关系型数据库
【多线程-从零开始-捌】阻塞队列,消费者生产者模型
【多线程-从零开始-捌】阻塞队列,消费者生产者模型
22 0
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
39 1
C++ 多线程之初识多线程