java多线程学习之14invokeLater()方法与invokeAndWait()的使用

invokeLater();与invokeAndWait();的使用:(这两个方法可以在"非"事件分派线程中被调用!)这两个方法都属于SwingUtilities类是两个静态成员方法(类方法!)
——————————————————————————————————————————————————————————————————————————————
swing技术下有个工具类:SwingUtilities这个类很奇特:
首先SwingUtilities.isEventDispatchThread()当其返回值是true时代表当前在jvm中运行的线程是“swing的事件派发线程”当返回值为false时代表当前在jvm中运行的thread不是事件分派线程(EDT:Event-Dispatch-Thread)。

SwingUtilities.invokeAndWait(Runnable xxx);方法可以在任何"非"事件分派线程的可执行方法的方法体中去调用,且该invokeAndWait(Runnable xxx);只能在"非"swing的事件分派线程中被调用!(不然会报error即java.lang.Error)!(权威)SwingUtilities.invokeAndWait(Runnable xxx);方法有个特性:他可以将该参数中的Runnable类型对象xxx委派给swing的事件分派线程,让swing的事件分派线程去隐蔽式地调用类似伪代码:Thread yyy=new Thread(xxx);yyy.start();即让事件派发线程去马上执行这个yyy,start();即这个xxx对象所描述的任务(task)而阻断当前jvm中运行的任何其他线程!

用通俗的话讲:SwingUtilities.invokeAndWait(Runnable xxx);方法可以让你自定义的一个Runnable 类型对象(task)(任务)委托给swing的事件分派线程,并让事件分派线程去无条件的执行该task(Runnable类型对象表示的任务,本例中为xxx即SwingUtilities.invokeAndWait(Runnable xxx);方法让事件派发线程去执行new Thread(xxx).start()注意这是为了便于理解的伪代码)并在该xxx对象(Runnable类型对象)的run()方法执行完毕之前阻塞当前调用了该SwingUtilities.invokeAndWait(Runnable xxx);方法的那个线程!直到该xxx对象的run()方法跑完并返回!(这句话的真正含义是SwingUtilities.invokeAndWait(Runnable xxx);方法是个同步化的方法,该法会等待目标任务(这里的这个目标任务指的是该Runnable类型对象xxx的run()方法体部分))被执行完毕后该invokeAndWait(Runnable xxx);方法才会被返回!)而且利用在SwingUtilities.invokeAndWait(Runnable xxx);方法中的此xxx对象的run()方法方法体可以调用swing组件对象(这就是invokeAndWait(Runnable xxx)方法的一大特性),因为虽然这个xxx对象是另一个独立的线程类型对象但是该xxx对象的run();方法实际上是在swing的事件派发线程中去间接地激活调用的,因此也就是间接地在swing的事件派发线程中调用了前文所提到的swing组件对象!从而没有违背只能在swing派发线程中直接或间接调用swing组件对象的黄金规则.(相当于通过间接地手法实现)


更通俗地讲:SwingUtilities.invokeAndWait(Runnable xxx);方法与SwingUtilities.invokeLater(Runnable xxx);能够让你自定义task(任务)Runnable xxx,并强制要求swing事件派发线程帮助你执行该task(帮助你执行该Runnable类型对象xxx的线程体可执行部分(run()方法体部分)),该方法最大特性是允许你在该作为invokeAndWait(Runnable xxx)方法参数的Runnable类型对象xxx的run();方法体内可调用swing技术下的系列组件对象(包括GUI组件对象),从而间接巧妙地实现了在“非”事件派发线程中(本例就是该Runnable xxx对象的线程体部分)实现了对swing组件对象的调用与使用!(权威)

使用SwingUtilities.invokeAndWait(Runnable xxx);方法要注意的事项:
SwingUtilities.invokeAndWait(Runnable xxx);是个阻塞性质的方法,它会在调用它的地方阻塞式地运行,即直到其(invokeAndWait(Runnable xxx);方法)所绑定的目标任务(Runnable xxx这个可执行对象(线程)的run()方法方法体部分)被执行完毕以后该SwingUtilities.invokeAndWait(Runnable xxx);方法才会被返回!(权威)
——————————————————————————————————————————————————————————————————————————————
SwingUtilities.invokeLater(Runnable xxx);方法剖析:
SwingUtilities.invokeLater(Runnable xxx);方法以Runnable类型对象作为参数,他将此Runnable类型对象 xxx 隐蔽式地传送给swing的事件派发线程,并让事件派发线程去隐含式地间接使该Runnable类型对象xxx的run();方法的方法体部分可以被执行!(让该run()方法得以执行!)
__________________________________________________________________________________________________________________________________________________________

SwingUtilities.invokeAndWait(Runnable xxx);方法与SwingUtilities.invokeLater(Runnable xxx)的区别:
SwingUtilities.invokeAndWait(Runnable xxx);方法与SwingUtilities.invokeLater(Runnable xxx)的功能大体上看来是相似地。
这两个方法有三个方面的区别:
第一:SwingUtilities.invokeLater(Runnable xxx)会在未来某个不确定的时间点上被异步地运行,你不知道它(SwingUtilities.invokeLater(Runnable xxx))何时才被真正地调用(运行),这就是Later之意义的体现!而SwingUtilities.invokeAndWait(Runnable xxx);是具有“同步”性质地,该方法会等到目标任务(该Runnable xxx类型线程)执行完毕以后才返回!因此SwingUtilities.invokeAndWait(Runnable xxx);在被调用时它是一个具有阻塞性质的方法!一般来说你用到的基本上都会是SwingUtilities.invokeAndWait(Runnable xxx);这个方法!

第二:SwingUtilities.invokeAndWait(Runnable xxx);这个方法不允许在事件派发线程中去显示地调用,即不能在事件处理函数(事件的回调函数)中直接书写调用SwingUtilities.invokeAndWait(Runnable xxx);方法!因此如果你在事件派发线程中直接调用SwingUtilities.invokeAndWait(Runnable xxx);方法系统会抛出java.lang.Error这会导致事件派发线程终止并离开,这样,整个程序会被异常停掉!而SwingUtilities.invokeLater(Runnable xxx);方法不会存在上述问题!


第三:如果调用了SwingUtilities.invokeAndWait(Runnable xxx);方法的那个"非"事件派发线程(这里用Thread notEDT代表)在swing的事件派发线程还没有运行该SwingUtilities.invokeAndWait(Runnable xxx);方法所委托的Runnable xxx(线程)这个目标任务时notEDT就被中断,则SwingUtilities.invokeAndWait(Runnable xxx);方法就会抛出InterruptedException这个异常,而若该Runnable 类型线程xxx抛出运行时的异常或错误的时候,该SwingUtilities.invokeAndWait(Runnable xxx);方法就会抛出
InvocationTargetException这个异常!

__________________________________________________________________________________________________________________________________________________________
一个常用的技巧(怎样防止在事件派发线程中错误的调用了SwingUtilities.invokeAndWait(Runnable xxx);方法)
答案:对当前jvm正在执行的线程做身份检查!
具体策略如下:即利用SwingUtilities.isEventDispatchThread()方法判断当前运行的线程是否是"swing的事件派发线程"若果不是(false)则在该线程中可以调用SwingUtilities.invokeAndWait(Runnable xxx);方法,否则(true)则千万不能在该线程(这里指的是事件派发线程)中调用SwingUtilities.invokeAndWait(Runnable xxx);方法这样做会报Error!

范例代码如下:
public class ScoreLabel extends JLabel implements CharacterListener{


.....
private void setScore(){
if(SwingUtilities.isEventDispatchThread())
{setText("在事件派发线程中调用了本Label类型对象")}

else{//利用以上的判断防止了在事件派发线程中调用invokeAndWait(Runnable xxx)方法!
try
{
SwingUtilities.invokeAndWait(new Runnable(){

public void run(){

setText("利用invokeAndWait(Runnable xxx)方法实现了在非事件派发线程中调用swing的GUI组件对象");


}


});


}
catch (InterruptedException ie){
ie.printTraceStack();

}
catch (InvocationTargetException ite){
ite.printTraceStack();
}

}

}

}

 

详见书 java Threads(英文版)
7.4 Long-Running Event Callbacks 
章节的案例代码!
Here's an example of how to take the first path and set up a thread in a long-running callback. Suppose that in our type tester, the start method must log into a server in order to get the data it is to display. You want to perform that operation in a separate thread because it may take a long time, during which you don't want the GUI to be unresponsive. In fact, you want to give the user an option to cancel that operation in case communicating with the server takes too long.

Here's a class that simulates connecting to the server. While it's at it, the frame displays some progress messages:

package javathreads.examples.ch07.example3;

import java.lang.reflect.*;

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

public class FeedbackFrame extends JFrame implements Runnable {

    private SwingTypeTester stt;

    private Thread t;

    private JLabel label;

    private int state;

    static String[] stateMessages = {

        "Connecting to server...",

        "Logging into server...",

        "Waiting for data...",

        "Complete"

    };

    public FeedbackFrame(SwingTypeTester stt) {

        this.stt = stt;

        setupFrame( );

        t = new Thread(this);

        t.start( );

        pack( );

        show( );

    }

    private void setupFrame( ) {

        label = new JLabel( );

        label.setPreferredSize(new Dimension(200, 200));

        Container c = getContentPane( );

        JButton stopButton = new JButton("Stop");

        stopButton.addActionListener(new ActionListener( ) {

            public void actionPerformed(ActionEvent ae) {

                error( );

            }

        });

        c.add(label, BorderLayout.NORTH);

        c.add(stopButton, BorderLayout.SOUTH);

    }

    private void setText(final String s) {

        try {

            SwingUtilities.invokeAndWait(new Runnable( ) {

                public void run( ) {

                    label.setText(s);

                }

            });

        } catch (InterruptedException ie) {

            error( );

        } catch (InvocationTargetException ite) {

            error( );

        }

    }

    private void error( ) {

        t.interrupt( );

        if (SwingUtilities.isEventDispatchThread( ))

            closeDown( );

        else SwingUtilities.invokeLater(new Runnable( ) {

            public void run( ) {

                closeDown( );

            }

        });

    }

    private void closeDown( ) {

        stt.setupCancelled( );

        hide( );

        dispose( );

    }

    public void run( ) {

        // Simulate connecting to server

        for (int i = 0; i < stateMessages.length; i++) {

            setText(stateMessages[i]);

            try {

                Thread.sleep(5 * 1000);

            } catch (InterruptedException ie) {}

            if (Thread.currentThread( ).isInterrupted( ))

                return;

         }

        SwingUtilities.invokeLater(new Runnable( ) {

            public void run( ) {

                stt.setupDone( );

                hide( );

                dispose( );

            }

        });

    }

}
 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值