2017-08-23 45 views
1

我有一个扩展JDialog的登录表单,用户可以通过刷卡或通过输入用户名和密码进行登录。从事件派发线程(EventQueue)停止另一个线程

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

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

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

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

编辑1:停止命令在刷卡登录时工作得很好。它优雅地结束了刷卡线程。在这种情况下,当前线程作用域位于CardSwipeThread上。

问题发生在手动登录时,当用户单击登录按钮时,当前有效范围的线程将是AWT-EventQueue-0或Event Dispatch Thread。将CardSwipeThread的volatile布尔值更新为false不会阻止其运行。

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

这是我的代码的一部分:

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; 
    } 
} 

回答

1

如果你的卡刷卡读卡器卡登录后停止,但手动登录后不停止,那么它在等待阻塞IO读取 卡,它甚至没有完成一次循环运行!它永远等待数据 ,所以甚至不能检查listenforswipe是否设置为 false!

你还是不会停止读卡线程,但根据您的应用程序的状态采取与读卡数据从卡读取线程的情况下适当的行动。

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

我以前的答案:

你在你的代码的其他部分的一些问题,CardSwipeThread的所有实例,当你调用停止(因为listenforswipeprivate static volatile boolean)要完成,有两个可能的原因是不停车:

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

  2. 的 “// Code for reading the magnetic stripe data.” 没有不散和probabl y阻止某些IO操作 - 等待数据被读取。如果是这样的话,那么如果你刷卡第二次也很可能完成CardSwipeThread - 但它取决于你如何执行这个代码,请张贴此代码,所以我们可以帮助您

而且如果您打算让其他人登录/ unlogin,那么为什么要停止CardSwipeThread守护进程?让它运行,只需在某人已经登录时正确提供卡片阅读事件,那么应该发生什么?这样,您就不必担心CardSwipeThread正在等待阻止某人输入卡的IO。

编辑:

使用SwingWorker在这里不解决任何问题,如果你调用cancel方法的想法,它无论如何都会等待要读取的数据。这可能导致新的SwingWorkerCardSwipeReader由于处理不当的资源而无法启动。这可能导致读卡器不可用。 这种SwingWorker方法甚至没有触及读卡代码中BlockingIO的实际问题。

+0

一次只能运行CardSwipeReader的一个实例,SwipeReader.java的目的是创建CardSwipeReader的新实例(如果它为null或仅返回存在的实例)。我没有包含读取磁条数据的代码,因为我认为它与我的问题无关,而且我只需要关闭CardSwipeThread。我需要停止CardSwipeThread,因为读者不仅可以用于登录,还可以进一步将信用付款用于应用程序。我在我的问题中添加了更多信息。 – ELM

+0

@ELM **显然,您在CardSwipeReader中阻止了IO,导致无法完成** –

+0

@ELM:读卡器API可能允许规定超时,以减轻此风险。 – trashgod

3

考虑在这种情况下使用SwingWorker<Boolean, Boolean>。在显示登录对话框之前,您需要​​工作人员。在您实施doInBackground()publish()查询结果时,如果它变得可用,请轮询读者。如果读卡器验证成功,则在event dispatch thread上执行的process()的执行可以在close对话框中执行。由于SwingWorker执行Future,如果键盘登录成功,您可以cancel()工作人员。有关其他示例,请参阅Worker Threads and SwingWorker标签。

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

我认为这是值得的努力,但是你会想在重新分解之前运行一些例子,因为它太积极。要查看数据流,请尝试从伪设备读取的example。要了解cancel()的工作原理,请尝试运行任意Processexample

+0

无论如何,如果他将取消SwingWorker,它无论如何都会等待数据被读取。这可能会导致无法启动带CardSwipeReader的新SwingWorker,因为资源处理不当。这可能会导致读卡器无法使用!这不是一个答案,它会带来更多的问题,而不是解决问题!这种SwingWorker方法甚至不触及真正的问题! –

+0

@KrzysztofCichocki:['SwingWorker''](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html)在这方面提供了几个优点。 – trashgod

+0

@trashgod,是SwingWorker很方便,但是它将如何解决读卡代码中的阻塞IO问题? –