2017-08-23 92 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問題? –