从事件调度线程(EventQueue)

时间:2017-08-23 05:48:25

标签: java multithreading swing awt-eventqueue

我有一个扩展JDialog的登录表单,用户可以通过刷卡或输入用户名和密码登录。

我创建了一个与磁条阅读器通信的Runnable守护进程,启动它时会请求刷卡,它会等到卡被刷卡。如果应用程序需要取消执行其他操作的请求,那么它将生成此线程将捕获的事件,从而取消等待刷卡的请求。

当用户刷卡时,应用程序将读取用户ID的轨道并验证它,如果验证成功,则会向刷卡守护程序发送停止命令并停止线程。

当用户输入用户名和密码时,swing包将访问响应登录按钮的click事件的线程(AWT-EventQueue-0)并继续评估登录凭据。

我的问题是每当应用程序在这个AWT-EventQueue-0线程上时,向刷卡守护进程发送停止事件将不起作用并且守护进程将保留在线程堆栈上。

编辑1:停止命令在刷卡登录时完全正常。它优雅地结束卡刷卡线程。在这种情况下,当前线程范围在CardSwipeThread上。

手动登录时会出现问题,当用户单击登录按钮时,当前作用域的线程将是AWT-EventQueue-0或Event Dispatch Thread。将CardSwipeThread的volatile布尔值更新为false不会阻止它运行。

编辑2: 读者与应用程序通信的唯一时间是刷卡时,手动登录时会出现问题而不需要刷卡。因此,由于IO操作被阻止,CardSwipeThread没有正常结束的问题。原来有一个隐藏在灌木丛后面。

这是我的代码的一部分:

LoginDialog.java

public class LoginDialog extends JDialog implements ActionListener, WindowListener
{
    public LoginDialog()
    {
        super();

        // ..More code that instantiates the objects of this JDialog.

        SwipeReader.getInstance().enable(true);
    }

    class SymAction implements java.awt.event.ActionListener
    {
        public void actionPerformed(java.awt.event.ActionEvent event)
        {
            Object object = event.getSource();
            if (object == logonButton)
            {
                logonButton_actionPerformed(event);
            }

            // ..More conditions for other objects.
        }
    }

    // The keyboard login method, does not terminate the card swipe daemon thread.
    void logonButton_actionPerformed(java.awt.event.ActionEvent event)
    {       
        try
        {
            // ..More code that will evaluate login information.

            if (authenticate == -1)
            {
                // Notify for failed login.
            }
            else if (authenticate == 0)
            {
                SwipeReader.getInstance().enable(false);
            }
        }
        catch (Exception e)
        {
            // Error logger.
        }
    }

    // The card swipe listener used for card login.
    public void deviceDataReceived(Object object)
    {   
        try
        {
            // ..More code that will evaluate login information.

            if (authenticate == -1)
            {
                // Notify for failed login.
            }

            if (authenticate == 0)
            {
                SwipeReader.getInstance().enable(false);
            }
        }
        catch (Exception e)
        {
            // Error logger.
        }
    }
}

SwipeReader.java

public class SwipeReader
{
    // This is a singleton class that creates the thread for the daemon.

    CardSwipeDaemon cardSwipeDaemon;
    Thread cardSwipeThread;
    SwipeReader instance;

    private SwipeReader() {}

    public static SwipeReader getInstance()
    {
        if (instance == null) { instance = new SwipeReader(); }
        return instance;
    }

    public void enable (boolean isEnabled)
    {
        if (isEnabled)
        {
            cardSwipeDaemon = new CardSwipeDaemon();
            cardSwipeThread = new Thread(cardSwipeDaemon, "CardSwipeThread");
            cardSwipeThread.start();
        }
        else
        {
            cardSwipeDaemon.stop();
            cardSwipeThread = null;
        }
    }
}

CardSwipeDaemon.java

public class CardSwipeDaemon implements Runnable
{
    CardSwipeDaemon instance;
    private static volatile boolean listenforswipe = true;

    public CardSwipeDaemon() {}

    public static synchronized CardSwipeDaemon getInstance()
    {
        if (instance == null) { instance = new CardSwipeDaemon(); }
        return instance;
    }

    public run()
    {
        listenforswipe = true;
        while (listenforswipe)
        {
            // Code for reading the magnetic stripe data.
        }
    }

    public void stop()
    {
        listenforswipe = false;
    }
}

2 个答案:

答案 0 :(得分:3)

在此上下文中考虑使用SwingWorker<Boolean, Boolean>。在显示登录对话框之前,您将execute()工作人员。如果结果可用,请在doInBackground()publish()的结果中轮询读者。如果读者身份验证成功,您在event dispatch thread上执行process()的实施可以close对话框。由于SwingWorker实现了Future,因此如果键盘登录成功,您可以cancel()工作人员。有关其他示例,请参阅Worker Threads and SwingWorker标记。

  

这需要我对代码进行一些修改。

我认为这是值得的,但是你想要在重新分解之前先运行一些例子。要查看数据流,请尝试从伪设备读取的example。要查看cancel()的工作原理,请尝试运行任意Process的{​​{3}}。

答案 1 :(得分:1)

  

如果您的卡片刷卡器在卡片登录后停止,但在手动登录后没有停止,则它正在等待阻止io读取   卡,它甚至没有完成一个循环运行!它等待数据   永远,所以甚至不能检查listenforswipe是否设置为   假<!/强>

您不应该停止读卡线程,但根据您的应用程序的状态,如果从读卡线程中读取卡片数据,请采取适当的措施。

这将解决阻止IO的问题,只需让读卡线程等待接收数据,并且在读取数据时将其发送到正确的目标,具体取决于您的应用程序状态。就像使用键盘一样 - 您不会启动和停止键盘 - 您只需更改焦点,因此键盘的输入会根据桌面的当前状态进入不同的目标。

我之前的回答:

你的代码的其他部分有一些问题,当你调用停止时,所有CardSwipeThread实例都应该完成(因为listenforswipeprivate static volatile boolean),有两个可能的原因导致它没有停止:

  1. 永远不会调用stop()方法 - 请调试它是否被调用

  2. &#34; // Code for reading the magnetic stripe data.&#34;没有结束,可能会阻止某些IO操作 - 等待读取数据。如果是这种情况,那么如果您第二次刷卡可能会完成CardSwipeThread - 但这取决于您如何实现此代码,请发布此代码,以便我们为您提供帮助

  3. 此外,如果您计划让其他人登录/取消登录,那么为什么要停止使用CardSwipeThread守护程序?让它运行,当有人已经登录时,只是正确地提供卡读取事件,那么应该怎么办?这样,您就不必担心CardSwipeThread正在等待阻止IO以便某人进入该卡。

    编辑:

    使用SwingWorker的想法并不能解决此处的任何问题,如果您调用cancel方法,它将会等待数据被读取。这可能会导致由于资源处理不当而无法启动SwingWorker CardSwipeReader新{{}}}的情况。这可能导致读卡器无法使用。 这种SwingWorker方法甚至无法触及读卡代码中BlockingIO的真正问题。