java并发编程实战-第9章-图形用户界面应用程序
虽然GUI框架本身是单线程子系统,但应用程序可能不是单线程的,因此在编写GUI代码的时候仍然需要谨慎
地考虑线程问题
9.1 为什么GUI是单线程的?
竞态条件和死锁,
1、气泡上升和气泡下沉,导致不一致的锁顺序,引发死锁
2、mvc模式的广泛使用,mvc三者的调用顺序也会造成不一致的锁定顺序,引发死锁
9.1.1 串行事件处理
事件是例外一种类型的任务,AWT,Swing的处理机制的结构类似于Excutor
只有单个线程,来处理所有的GUI任务,处理完一个接着再处理下一个。但不利之处在于:如果某个任务执
行事件很长,则会造成程序响应性问题。
所以在启动一些执行事件较长的任务时,比如,拼写检查、文件搜索或者网络获取资源,必须重启另外一个
线程中执行,尽快把控制权交给事件线程。
9.1.2 Swing中的线程封闭机制
所有Swing(例如JButton和JTable)组件和数据模型对象(TableModel 和 TreeModel) 都封闭在事件线程
中.
swing单线程规则:The Swing single-thread rule: Swing components and models should be created,
modified, and queried only from the event-dispatching thread.
如何调度:程序清单GuiExecutor类似于Swing的invokeLater,把任务调度到事件线程中执行
9.2 短事件的GUI任务
只要任务是短期的,并且只访问GUI对象,那么基本可以忽略与线程相关的问题
9.3 长时间的GUI任务 (重点理解线程接力方式)
事件线程-提交任务到后台线程,后台线程完成后,再提交另一个任务给事件线程来更新用户界面
Listing 9.5. Long-running Task with User Feedback.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
label.setText("busy");
backgroundExec.execute(new Runnable() {
public void run() {
try {
doBigComputation();
} finally {
GuiExecutor.instance().execute(new Runnable() { //提交另一个任务给事件线程
来更新用户界面
public void run() {
button.setEnabled(true);
label.setText("idle");
}
});
}
}
});
}
});
9.3.1 取消
使用Future
Listing 9.6. Cancelling a Long-running Task.
Future<?> runningTask = null; // thread-confined
...
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (runningTask != null) {
runningTask = backgroundExec.submit(new Runnable() {
public void run() {
while (moreWork()) {
if (Thread.currentThread().isInterrupted()) {
cleanUpPartialWork();
break;
}
doSomeWork();
}
}
});
};
}});
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
if (runningTask != null)
runningTask.cancel(true);
}});
9.3.2 进度标志和完成标志
类似Future的取消操作,FutureTask有个done方法来响应完成事件,当后台的Callable完成后,将调用done
。
Listing 9.7. Background Task Class Supporting Cancellation, Completion Notification, and
Progress Notification.
abstract class BackgroundTask<V> implements Runnable, Future<V> {
private final FutureTask<V> computation = new Computation();
private class Computation extends FutureTask<V> {
public Computation() {
super(new Callable<V>() {
public V call() throws Exception {
return BackgroundTask.this.compute() ;
}
});
}
protected final void done() { //长时间任务完成后,向事件线程提交任务更新界面
GuiExecutor.instance().execute(new Runnable() {
public void run() {
V value = null;
Throwable thrown = null;
boolean cancelled = false;
try {
value = get();
} catch (ExecutionException e) {
thrown = e.getCause();
} catch (CancellationException e) {
cancelled = true;
} catch (InterruptedException consumed) {
} finally {
onCompletion(value, thrown, cancelled);
}
};
});
}
}
protected void setProgress(final int current, final int max) {
GuiExecutor.instance().execute(new Runnable() {
public void run() { onProgress(current, max); }
});
}
// Called in the background thread
protected abstract V compute() throws Exception;
// Called in the event thread
protected void onCompletion(V result, Throwable exception,
boolean cancelled) { }
protected void onProgress(int current, int max) { }
// Other Future methods forwarded to computation
}
Listing 9.8. Initiating a Long-running, Cancellable Task with BackgroundTask.
public void runInBackground(final Runnable task) {
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
class CancelListener implements ActionListener {
BackgroundTask<?> task;
public void actionPerformed(ActionEvent event) {
if (task != null)
task.cancel(true);
}
}
final CancelListener listener = new CancelListener();
listener.task = new BackgroundTask<Void>() {
public Void compute() {
while (moreWork() && !isCancelled())
doSomeWork();
return null;
}
public void onCompletion(boolean cancelled, String s,
Throwable exception) {
cancelButton.removeActionListener(listener);
label.setText("done");
}
};
cancelButton.addActionListener(listener);
backgroundExec.execute(task);
}
});
}
9.3.3 SwingWorker
Swing中执行长时间任务的框架,包括取消、完成通知、进度指示等。
9.4 共享数据模型
树形控件,也可以使用线程安全的树形模型来实现这个功能:通过invokerLater提交一个任务,将数据从后
台任务“推入”事件线程,或者让事件线程池通过轮询来查看是否有数据可以用。
9.4.1 线程安全的数据模型
如果数据模型支持细粒度的并发,那么时间线程和后台线程就能共享该数据模型,而不会发生响应问题。
例如第五章的 DelegatingVehicleTracker 使用ConcurrentHashMap提高并发的读写操作,但缺点在于,无
法提供一致性的数据快照。
有时候,使用版本化数据模型时,例如CopyOnWriteArrayList,虽然能提供一致性的快照,但得到的快照可
能是一个老版本了。CopyOnWriteArrayList适合在遍历操远远多于修改操作时,才能发挥更好的性能,这显
然在车辆跟踪应用程序中不适合该方法
但要实现既要提供并发访问,又要提供最新版本的数据结构并不容易。。。。。。。。
9.4.2 分解数据模型
如果程序中既包含表示的数据模型,又有程序特定的数据模型,那么该应用程序拥有一种分解模型设计
表示模型被封闭在事件线程中,其他模型,即共享模型,是线程安全的。即可以由事件线程方法,也可以由
应用程序其他线程访问
Consider a split-model design when a data model must be shared by more than one thread and
implementing a thread-safe data model would be inadvisable because of blocking, consistency,
or complexity reasons.
由于阻塞、一致性和复杂度等原因和当一个数据模型必须被多个线程共享的时候,应该考虑一个分解模型设
计而不是一个线程安全的数据模型。
9.5 其他形式的单线程子系统
例如对原生库(native libraries) 的访问等,框架参考程序清单9-1 和 9-2
Listing 9.1. Implementing SwingUtilities Using an Executor.
public class SwingUtilities {
private static final ExecutorService exec =
Executors.newSingleThreadExecutor(new SwingThreadFactory());
private static volatile Thread swingThread;
private static class SwingThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
swingThread = new Thread(r);
return swingThread;
}
}
public static boolean isEventDispatchThread() {
return Thread.currentThread() == swingThread;
}
public static void invokeLater(Runnable task) {
exec.execute(task);
}
public static void invokeAndWait(Runnable task)
throws InterruptedException, InvocationTargetException {
Future f = exec.submit(task);
try {
f.get();
} catch (ExecutionException e) {
throw new InvocationTargetException(e);
}
}
}
使用上面的工具
Listing 9.2. Executor Built Atop SwingUtilities.
public class GuiExecutor extends AbstractExecutorService {
// Singletons have a private constructor and a public factory
private static final GuiExecutor instance = new GuiExecutor();
private GuiExecutor() { }
public static GuiExecutor instance() { return instance; }
public void execute(Runnable r) {
if (SwingUtilities.isEventDispatchThread())
r.run();
else
SwingUtilities.invokeLater(r);
}
// Plus trivial implementations of lifecycle methods
}
小结:
所有的GUI框架基本都是实现单线程的子系统。所有与表现有关的代码都作为任务在事件线程中运行。
当有一个运行时间长的任务时,提交一个任务在后台线程运行,结束后,又提交一个任务到事件线程中运行
(线程接力)。
相关推荐
第9章 图形用户界面应用程序 第三部分 活跃性、性能与测试 第10章 避免活跃性危险 第11章 性能与可伸缩性 第12章 并发程序的测试 第四部分 高级主题 第13章 显式锁 第14章 构建自定义的同步工具 第15章 ...
11. **Swing和JavaFX**:了解GUI(图形用户界面)开发,学习使用Swing库创建桌面应用程序,或者使用JavaFX构建现代、丰富的用户界面。 12. **Java EE技术**:深入学习Servlet、JSP、JNDI、EJB、JMS等Java企业级开发...
9. **Swing与AWT**:对于GUI(图形用户界面)编程,Java提供了Swing和AWT库。书中会介绍如何创建窗口、按钮、文本框等组件,以及事件处理。 10. **Java应用开发**:除了基本的编程技巧,书中的实例可能还会涵盖Java...
10. **第十章**:可能涉及图形用户界面(GUI)编程,如Swing组件的使用、事件处理等。 11. **第十一章**:可能涵盖Java的泛型,用于提高代码的复用性和安全性。 12. **第十二章**:可能讲解Java的文件和数据库操作,...
- **Swing与AWT图形用户界面开发**:演示如何构建美观且功能丰富的GUI应用程序。 - **高级类库使用技巧**:深入探索Java集合框架、日期时间API等高级类库的使用方法。 **7. 实战案例研究** - 本书可能会通过一...
3. **用户界面**:银行排号系统需要友好的用户界面,可能使用了JavaFX或Swing进行图形用户界面(GUI)的开发,以实现用户交互,如取号、查看当前队列状态等功能。 4. **多线程技术**:为了实现并发处理,系统可能会...
10. **Swing和JavaFX图形界面**:Java提供了Swing和JavaFX两个库用于构建桌面应用程序的用户界面。了解组件的使用、布局管理器以及事件处理机制,可以帮助创建功能丰富的图形化应用。 11. **Java注解(Annotation)...
《Java开发实战1200例》是一本深入浅出的Java编程教程,它通过1200个实例,全面覆盖了Java语言的基础知识、面向对象编程、异常处理、集合框架、多线程、网络编程、数据库操作、图形用户界面设计、Swing应用、XML处理...
《疯狂Java实战演义》是一本深度探讨Java编程技术的实战书籍,虽然提及缺少第五章的内容,但从其他章节的标题可以看出这本书涵盖了广泛的Java应用领域。以下是各章节的主要知识点概述: 1. **第7章 - 单机连连看** ...
11. **第11章:图形用户界面编程** - Swing组件库介绍 - AWT与Swing的比较 12. **第12章:网络编程** - Socket编程模型 - HTTP协议及其实现方式 13. **第13章:数据库访问** - JDBC API介绍 - 数据库连接...
第9章“多线程”介绍了并发编程的基础,包括线程的创建、同步、互斥等,使学习者能够编写出能够充分利用多核处理器的程序。 第10章“图形用户界面设计”涉及Swing或JavaFX库,讲解如何创建窗口应用,添加控件,响应...
8. **Java Swing和JavaFX**:是Java提供的图形用户界面(GUI)工具包,用于创建桌面应用程序。学习如何使用JFrame、JButton、JLabel等组件,以及事件处理,可以创建交互式的应用。 9. **Java集合框架的高级主题**:...
10. **Java API和标准库**:Java提供了丰富的API,如AWT和Swing用于图形用户界面开发,JDBC用于数据库连接,JAXP和DOM/SAX用于XML处理等。 11. **实战项目**:教程可能会包含一些实践项目,如简单的计算器、文件...
9. **Java Swing和JavaFX**:这两部分主要讲解Java的图形用户界面(GUI)开发,Swing是传统的GUI库,而JavaFX是现代且功能强大的UI框架,适合创建桌面应用程序。 10. **Java EE相关**:虽然书名并未明确提及,但...
6. **多线程**:030901_【第9章:多线程】_认识多线程笔记.pdf,讲述了Java中的并发编程,如何创建和管理线程,理解线程同步与通信的概念,以及Java提供的线程API。 7. **图形界面**:031809_【第18章:图形界面】_...
- **Swing组件**:学习如何使用Swing库构建图形用户界面。 - **事件处理**:介绍如何为按钮、文本框等组件添加监听器来响应用户的操作。 - **布局管理器**:如FlowLayout、BorderLayout等,用于控制组件在容器中的...
图形用户界面 (Lesson8) - **Swing组件**:介绍Swing框架下的基本组件,如按钮、文本框等。 - **布局管理器**:学习如何使用布局管理器来组织Swing组件,以实现灵活的窗口布局。 ##### 9. UI事件处理 (Lesson9) ...
6. **Java Swing和JavaFX**:用于构建图形用户界面(GUI)的应用程序,让开发者可以创建功能丰富的桌面应用。 7. **Java EE**:针对企业级应用开发的扩展,包括Servlet、JSP、EJB、JPA等技术。 8. **反射和动态...
9. **Swing和JavaFX图形界面**:Java提供了Swing和JavaFX两个库用于构建桌面应用的用户界面,书中可能会介绍组件使用、事件处理、布局管理等。 10. **Java EE应用开发**:虽然标题未明确提及,但作为实战指南,可能...