java多线程学习之10 Thread在java Swing技术中的使用


预言:
java的swing技术下的各种gui组件对象都不是线程安全的即如果存在多个线程并发访问同一个swing技术下的gui组件对象,可能会遭到程序运行异常,gui界面挂起等其他以外的错误结果!(换句话说:swing技术下同一个gui组件对象不支持多线程对它的并发访问!)(切记切记这是使用原则!),要正确设计有关使用swing技术的gui交互程序!一个重要且必须遵守的原则技巧是:"利用一个特定独立的thread(线程)来独自的访问Swing的各种组件对象!"(必须使用一个特例独行的线程来独自负责对Swing组件对象的访问!)___这个线程对象就是Swing技术下内置的"事件派发线程"(一个swing技术内置的独立运作的线程),由此议题引伸出一个理论:所有其他不具备线程安全的java对象(不能够被多线程并发访问的java对象,即不支持多线程对其并发访问的java"资源"对象)对于它的访问的最佳方式就是为其开辟一个独立运行的线程(单独的only one)个线程去独自的访问它,处理它(独裁式处理)!即通过单一的thread来访问无多线程访问安全性的java对象!这与swing对象必须从一个单独的特殊设计的单一的特例独行的thread(事件派发线程)中去访问,在原理上是如出一辙的!!
__________________________________________________________________________________________________________________________________________________
那么为什么要用一个独立运行的线程(事件派发线程)来独自处理与访问java Swing技术下的各种GUI组件与事件对象(Event类型对象)呢?
答案:原因在于swing技术并没有采用"数据同步访问技术"来访问swing对象中的内部各种复杂的内在状态(内在属性即成员属性)!换句话说swing没有采用"同步"的技术方式来访问swing技术中的各种对象的内部成员属性,这就会造成一种情况:当多线程试图访问同一个swing对象的时候,由于该swing对象的各个成员属性的访问方式不支持"数据的同步访问模式"即不支持"针对数据的多线程的安全的并发访问",因此当有多线程访问同一个swing对象时由于该swing对象的内部状态数据不是"线程安全的,即不支持被多线程同步访问"(未被设计为可被多线程同步访问),因此有可能造成多线程可对其进行"脏读""脏写"现象(数据,状态的不一致现象),因此为了避免这一灾难性的结果发生,swing技术采用了使用完全独立的一个特殊线程(事件派发线程)来独自单独地对swing对象进行访问与处理这样就不会引发"多线程"对同一个swing对象的并发访问现象了,因为swing对象完全由"事件派发线程"独裁式处理与访问了,其他线程只能靠边站了,否则就会出错报error!这是java的swing技术的独特设计所造成的!

_____________________________________________________________________________________________________________________________________________
针对"事件派发线程"使用的黄金规则:(必须知道必须遵守)
一:
Swing组件类都遵循着MVC(MODEL-VIEW-CONTROL)这种设计模式!swing组件内部的各种状态值的设定(成员属性值的设定)是非常复杂的!当model层的代码(业务逻辑层功能代码)被事件派发线程调用的“事件处理函数的函数体”中调用时!你企图用其他的线程去访问该swing组件对象是件很危险的事情(不是报错就是异常),因为即使是最简单的swing组件也带有复杂的状态!因此使用swing技术时决不能接受从任何一个"非"事件派发线程中去调用或访问swing组件对象让该swing组件对象去调用它自己的成员方法!因此,对swing组件对象的调用都必须在事件派发线程中进行(事件派发线程是swing对象用来改变其内部状态(成员属性值)的特定工具),且是利用事件派发线程对事件处理器函数的隐式调用中进行!即必须在事件处理函数的函数体内对swing组件可以进行调用,这是因为对事件处理函数的调用都是由事件派发线程隐含式(隐蔽式)的偷偷进行的!因此在事件处理函数中调用swing对象就是隐含式的间接地让事件派发线程隐式地处理与调用(访问)该swing对象!这完全符合swing技术的设计标准!(权威),因此,只要你对swing对象的调用都是来自于事件派发线程,就不会存在对该swing对象访问的资源竞争(race condition)问题,因为它(该swing对象)是由单一线程(这里指的是事件派发线程)单一处理的!(独裁式处理与访问的)。(这就是使用Thread与swing技术交互使用的黄金规则)

注意:针对以上该黄金规则有四个特殊情况:
第一:没有在屏幕上显示出来的swing组件对象(没有setVisibile(false)即noDisplay)的swing的GUI组件对象可以被任何的线程(thread)创建与操作.这代表的含义是你可以在任何的thread中创建GUI对象!但是若所创建的GUI对象已经在屏幕上显示出来了(display或setVisibile(true))(只该GUI对象已经具备了可见性操作),那么这些已经具备了可见性操作能力的GUI对象就只能够通过事件派发线程去访问了!(权威),切记,gui对象是在它的父容器对象(父frame)对象的show();方法被调用后具有了可见性操作性的,即GUI对象在其父frame对象调用show();方法后该GUI对象就被显示出来了,即此时该GUI对象就仅仅只能通过swing的事件派发线程去访问与操作了!

第二:repaint();方法可以从任何一个thread中去调用!

第三:invokeLater();方法可以从任何的thread中去调用!

第四:invokeAndWait();方法可以从任何"非"事件派发线程的其他线程中去调用!
__________________________________________________________________________________________________________________________________________________

事件派发线程的处理:
如同我们之前提过的,所有Swing程序的事件回调(对事件处理函数的调用)都放生在事件派发线程中(都是由事件派发线程在其run();方法中隐蔽式地自动调用!),这是个好消息,它意味着大部分访问Swing组件的程序代码都是自动地在事件派发线程中被间接地调用!因为访问Swing组件的程序代码通常都写在事件回调函数(事件处理器函数)的函数体内!而事件处理器函数(事件回调函数)正是被隐式调用在事件派发线程的run()方法中!swing技术中的各个class都已经确保了所有的回调(事件处理)都会发生在事件派发线程上!例如:preferredSize();paintComponent();keyPressed();actionPerformed();等方法都是常见的回调函数(事件处理函数),所以你不必担心对他们的使用!而initComponents()方法并不是事件处理函数(回调函数),因此它可以在其他任何一个thread中被运行(被调用)

_____________________________________________________________________________________________________________________________________________
一:什么是事件派发线程
答:Java的Swing技术下:事件的激活与响应是由一个叫做事件派发线程的独立线程来隐式处理的!(这个线程的名字叫event-dispatching-thread),这个线程会运行并调用所有与事件(event)有关的回调(callback)函数!例如:常见的回调函数有actionPerformed();keyPress()函数等(都是事件处理函数)(注意:对于所有的Swing对象的访问必须都发生在事件派发线程中,即都由该线程负责访问!)
________________________________________________________________________________________________________
什么是回调函数,或者说是事件对应的回调函数?
答案:
回调函数就是通常所说的事件处理器函数(即事件处理函数!)例如:常见的回调函数有actionPerformed();keyPress()函数等(都是事件处理函数)
_________________________________________________________________________________________________________________________
专题:<<长时间运行的事件回调函数的处理与设计>>
设计思想:Swing程序与thread的交互有一种典型的情况:"长时间运行的事件回调"(事件处理函数中函数体中代码指令运行的耗时过大,过长),此时当响应事件的回调函数(事件处理器函数)运行的时候,其余的GUI组件对象是没有响应的(与用户没有可以继续的互动)。如果这种状态会持续很长的一段时间,它就可能会让用户感到沮丧和受挫,用户会怀疑程序已经挂掉了,无法运行了!(这通常是个错觉),因此长时间运行的任务(task)应该被设计在一个独立的thread上让其执行(隐式执行),这样不会阻断用户的其他操作,使用户感到相对愉悦,无挫折感!
_______________________________________________________________________________________________________________________
<<长时间运行的事件回调函数的处理与设计方案>>
长时间运行的任务(task)应该被设计在一个独立的thread上让其执行(隐式执行),这样不会阻断用户的其他操作,使用户感到相对愉悦,无挫折感!
针对上述理论:有几种相关的设计方案:如果你有许多像这样的自定义任务且各个任务的耗时很长,那么最简单的实现方式是使用 Thread pool(线程池)或者task scheduler(任务计划,或者说“计划任务”),使用的原则是:当有许多的task(任务)要并行地执行,那么你可以使用thread pool(线程池策略)来实现!而如果你只有一个耗时的任务one task且必须在一个指定的时间点(或可预测的指定的时间段)内要完成时或者要定期(固定周期)被执行时,那么请你使用task scheduler(计划任务策略)来实现此功能!(权威)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值