在我们这批新人转正评审的时候,我师父问了我的小伙伴一个问题:为什么一些更新界面的方法只能在主线程中调用?师父没有问我这个问题,让知其然但不知其所以然的我有种侥幸逃过一难的心情。我想如果回答那是因为Android GUI库是单线程消息机制的,更新界面的操作必须放到主线程中执行,那师父可能继续问为什么Android GUI要设计成单线程的,我就不知道了。
为什么它非得设计为单线程的?多线程不是更好吗?带着点好奇感和求知欲以及鄙视权威的无畏精神我在google中展开了搜索,并最终找到了一个令我满意的解释,欣喜之余将我的理解分析给大家。
单线程消息队列机制
首先我还是说一下我对GUI单线程消息队列机制的理解,这是我大学里几年编程经验赚来的知其然的部分。
Android、Swing、MFC等的GUI库都使用单线程消息队列机制来处理绘制界面、事件响应等消息,在这种设计中,每个待处理的任务都被封装成一个消息添加到消息队列中。消息队列是线程安全的(消息队列自己通过加锁等机制保证消息不会在多线程竞争中丢失),任何线程都可以添加消息到这个队列中,但是只有主线程(UI线程)从中取出消息,并执行消息的响应函数,这就保证了只有主线程才去执行这些操作。
单线程消息队列机制存在一个问题:消息响应函数中不能有耗时长的、计算密集型的操作,因为主线程在努力地处理这样的操作的时候就无法去处理其它的积压在消息队列中的绘制消息、事件消息了(一个消息处理完了主线程才会去队列中取下一个消息),这时候就会出现按键无响应、点击无反应的情况。
但这个问题有完美的解决方案,我们可以在消息响应函数中启动另一个工作线程(Worker Thread)来执行耗时操作,这样在线程启动起来后这个消息就算处理完了,主线程可以取下一个消息了,这时候主线程和还未执行完计算任务的工作线程就在操作系统的调度下并驾齐驱地狂奔了(调度算法会保证两个线程并发或并行地执行,不会专宠某个线程)。
一般我们在耗时任务执行完后还要更新界面展示计算的结果,由于我们不能直接在工作线程中更新界面,所以可能有些小伙伴直接在消息响应函数中线程start后就接着调用join来等待线程结束以更新界面,这其实相当于把耗时任务直接放在主线程去执行,因为在消息响应函数中join其实就是主线程在join,积压的消息是得不到处理的。正确的处理办法是将耗时任务改为异步通知机制,即工作线程向消息队列中添加消息以通知主线程耗时任务完成了,这样主线程在启动工作线程后就不需要主动地去调查任务的进展了,“任务结束的时候它会通知我的”,主线程如是说。
工作线程向主线程的消息队列添加消息的常用方法如下:
l Android:Acitvity.runOnUiThead、Handler.post、AsyncTask
l Swing:SwingUtilities.invokeLater
l Win32、MFC:自定义用户消息,在工作线程中PostMessage
GUI为什么不设计为多线程
大部分的GUI toolkits都是设计为上面的单线程消息队列机制,为什么不设计为多线程的呢?如果GUI是多线程的,CPU又是多核的话,多个CPU核心可以并行地执行绘制等操作,界面响应的速度应该是成倍提升的;而且就算是其中有多线程共享的资源加锁不就行了吗?
在google搜索的过程中我看到了负责Swing开发的一个大师的一篇博客《Multithreaded toolkits: A failed dream?》:
https://weblogs.java.net/blog/kgh/archive/2004/10/multithreaded_t.html
从中我了解到开发多线程的GUI toolkits是一件吃力不讨好的事,不仅开发难度大Bug多多,用起来也未必可以获得理想中的效果,其中的死锁和竞争,大师们也感到头疼。
多线程GUI加锁困难
为什么这么困难?大师讲了一个例子,我们通过用户级的代码去改变界面如TextView.setText走的是个自顶向下的流程:
而系统底层发起的如键盘事件、点击事件走的是个自底向上的流程:
这样就麻烦了,因为为了避免死锁,每个流程都要走一样的加锁顺序,而GUI中的这两个流程却是完全相反的,如果每一层都有一个锁的话加锁就是个难以完成的任务了,而如果每一层都共用一个锁的话,那就跟单线程没区别了。
于是GUI toolkits的开发者就“不负责任”地把GUI设计成了单线程消息队列机制,然后他们还说界面更新一般不是瓶颈,单线程足够了。然后我瞬间想到了3D游戏,单线程对于3D应该是很吃力的,但实际上负责3D绘制的是显卡的GPU,GPU不像CPU那样事无巨细、事必亲躬、鞠躬尽瘁、死而后已,只负责画好它的图就可以了,所以并行起来不是件困难的事。
相关推荐
基于Java多线程和GUI的贪吃蛇,本人自己设计的全新界面,带游戏音效,带答辩PPT,保证能用,仅需要2积分,算是对博主的知识成果打赏吧
java课程设计模拟交通灯。
java多线程聊天程序GUI界面socket实现.rar
基于Java的GUI多线程下载器的设计与实现.pdf
基于Java的GUI多线程下载器的设计与实现
掌握Java中多线程的编程,Thread类,Runnable接口的使用。 4.掌握用面向对象的方法分析和解决复杂问题。 二.实验内容 编写程序完成以下功能: 1. 设计一个基于GUI的客户-服务器的通信应用程序,如图1,图2所示。 ...
面向对象与多线程综合实验做的档案管理系统,包含三类使用人员:系统管理员,档案管理员,普通用户。一共要做七次迭代,每次迭代完成的任务不同。
网上找了份资料,是别人完成的Java实现多线程下载的功能。Java多线程的好处挺多的,可以充分利用CPU的资源,简化编程模型,简化异步事件的处理,使GUI更有效率,节约成本...
1.利用Socket通信机制实现一个多线程的端口扫描器。 2.设计要求: 2.1用户界面:用户可以输入IP地址或IP地址段;输入端口号或端口号范围;列表显示主机名、开放的端口及开放端口上相应的服务名称。 2.2端口的有效...
WHUT-java多线程实验-第四周-GUI(IDEA实现)
采用java的GUI,多线程,I/O及网络编程技术,编写了实时聊天程序, 类似QQ功能.
本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)...进阶篇有反射、泛型、注解、网络编程、多线程、序列化、数据库、Servlet、JSP、XML解析、单例模式与枚举。本专栏主要为Java入门者提供实验参考。
嵌入式C++ QT GUI设计。 项目代码可直接编译运行。
matlab GUI 简单设计实现 附图 有简单的界面和功能的实现
java毕业设计
西南科技大学Java程序设计与实践 实验一、实验目的: 练习面向对象的图形用户界面设计、界面布局、事件设计方 法。 二、实验内容: 设计一个类似Windows计算器的Application应用程序。 三、实验要求: 1、采用Java ...
聊天室-GUI设计 1、若未学网络通信与多线程,可暂且通过对象调用来完成。 2、打包JAR。 3、工程文件打包上传。 4、实现网络编程后,重新提交最后的代码与运行截图。
Java面向对象与多线程综合实验(四)之GUI设计-附件资源
基于java多媒体游戏软件的设计,游戏是扑克牌三公规则,用到常用GUI控制组件及其事件处理,多线程编程的应用和图形控件的布局使用等.
运用面向对象程序设计思想,基于Java文件管理、输入输出系统、多线程、网络通信、GUI程序设计,实现远程文件管理系统。