2011-03-13 49 views
3

基本上,我打電話給BufferedReader.ReadLine();然而,我正在一個多線程服務器中,在這裏我正在同步樹中的一個節點。所以當這個ReadLine函數被調用時,如果有其他人到達該節點,它們被鎖定。我無法弄清楚如何在退出線程之前等待ReadLine等待響應的時間限制。我得到的最接近的是創建一個可以睡1ms的新線程,然後檢查設置ReadLine的變量是否更改。因此,像這樣:如何超時阻塞Java的函數?

synchronized (pointer) { 
    String answer = ""; 
    Thread d = new Thread(new Runnable() { 
     public void run() { 
      try { 
       int i = 0; 
       while (answer.equals("")) { 
        if (i == 10000) { 
         System.out.println("Timeout Occured"); 
         System.exit(0); 
        } 
        try { 
         Thread.sleep(1); 
         i++; 
        } 
        catch(Exception e) { 
         System.out.println("sleep problem occured"); 
        } 
       } 
      } 
      catch (IOException ex) { 
      } 
     }  
    }); 

    d.start(); 
    answer = socketIn.readLine(); 
} 

這做了什麼,我想它,但我無法弄清楚如何停止當前線程解鎖節點,以便其他用戶可以繼續,而不是殺死整個服務器。最後,我想也許我可以這樣做:

Thread d = new Thread(new Runnable() { 
     public void run() { 
      try { 
       answer = socketIn.readLine(); 
      } catch (IOException ex) { 
      } 
     }  
    }); 

    d.join(10000); 
    catch (InterruptedException e){ 
    socketOut.println("Timeout Occured. Returning you to the beginning..."); 
    socketOut.flush(); 
    return; 
} 

但是,這似乎仍然阻止,無法繼續。有人可以幫我解決這個問題嗎?我無法理解我在做什麼錯了?

我也嘗試讓ExecutorService工作,但不能。這是我的答案嗎?我將如何實現它?

[編輯] socketIn是一個BufferedReader,應該說明確抱歉。 此外,客戶端通過telnet連接,但我認爲不重要。

我在這裏做的是一個「名人猜測遊戲」,用戶可以將名人添加到樹中。所以我需要鎖定該人員正在編輯的節點以保證線程安全

+0

請參閱下面的回答 - 如果您在等待輸入時鎖定,則會卡住(除非您超時讀取,在這種情況下,您只是緩慢)。你上面的代碼更危險,因爲你正在開始一個新線程,並試圖讓兩個線程在不鎖定的情況下訪問'answer'(同步)。 – 2011-03-13 20:28:42

回答

3

這是功課嗎?這很可能接近昨天其他人提出的問題。如果是的話,它應該有作業標籤。

當線程修改其他線程可能讀取/修改的數據時,您只需鎖定某些內容。

如果您正在鎖定等待輸入的內容,則鎖的範圍太廣泛。

你的流程應該是:

  • 從客戶端讀取輸入(阻塞的readLine())
  • 鎖定共享資源
  • 修改
  • 解鎖

(這是假設你每個連接/客戶端都有一個線程,並且在從客戶端讀取時阻塞)

這就是說...如果您正在從套接字讀取並希望超時,則在您首次接受連接時需要使用clientSocket.setSoTimeout(1000);。如果您BufferedReader等待的時間量(以毫秒爲單位),並沒有得到輸入時,它會拋出一個java.net.SocketTimeoutException

String inputLine = null; 
try 
{ 
    inputLine = in.readLine(); 
    if (inputLine == null) 
    { 
     System.out.println("Client Disconnected!"); 
    } 
    else 
    { 
     // I have input, do something with it 
    } 
} 
catch(java.net.SocketTimeoutException e) 
{ 
    System.out.println("Timed out trying to read from socket"); 
} 
+0

downvote?小心解釋爲什麼? – 2011-03-14 05:46:31

+0

我沒有這樣做... – Jeff 2011-03-14 15:49:09

0

我想出了一個解決方案:

answer = ""; 
try{ 
    Thread d = new Thread(new Runnable() { 
     public void run() { 
      try { 
       answer = socketIn.readLine(); 
      } 
      catch (IOException ex) { 
       System.out.println("IO exception occurred"); 
      } 
     } 
    }); 
    d.join(10000); //Not sure if this is superfluous or not, but it didn't seem to work without it. 
    d.start(); 
    i = 0; 
    while (true){ 
     if (i == 10000){ 
      if (d.isAlive()) throw InterruptedException; 
     } 
     if (answer.equals("")){ 
      Thread.sleep(1); 
     } 
     else{ 
      break; 
     } 
     i++; 
    } 
    //This essentially acts as Thread.sleep(10000), but the way I 
    //implemented it, it checks to see if answer is modified or not every 
    //.001 seconds. It will run for just over 10 seconds because of these checks. 
    //The number in Thread.sleep could probably be higher, as it is 
    //unnecessary to check so frequently and it will make the code more efficient 
    //Once it hits 10000, it throws an exception to move to the catch block below 
    //where the catch block returns everything to its original state and 
    //returns the client to the beginning 
    } 
catch (Exception e){ 
    socketOut.println("Timeout Occurred. Returning you to the beginning..."); 
    socketOut.flush(); 
    pointer = tree.root; 
    //reset class members to their original state 
    return; 
} 

感謝看着它,布賴恩羅奇。你的文章是內容豐富的,但我意外地忽略了一些關鍵信息。我會盡量在將來更小心。

1

一切都已完成。嘗試使用java.util.concurrent

// 
    // 1. construct reading task 
    // 
    final FutureTask<String> readLine = new FutureTask<String> (
     new Callable<String>() { 
      @Override public String call() throws Exception { 
       return socketIn.readLine(); 
      } 
     } 
    ); 
    // 
    // 2. wrap it with "timed logic" 
    // *** remember: you expose to your users only this task 
    // 
    final FutureTask<String> timedReadLine = new FutureTask<String> (
     new Callable<String>() { 
      @Override public String call() throws Exception { 
       try { 
        // 
        // you give reading task a time budget: 
        //  try to get a result for not more than 1 minute 
        // 
        return readLine.get(1, TimeUnit.MINUTES); 
       } finally { 
        // 
        // regardless of the result you MUST interrupt readLine task 
        // otherwise it might run forever 
        //  *** if it is already done nothing bad will happen 
        // 
        readLine.cancel(true); 
       } 
      } 
     } 
    ) 
    { 
     // 
     // you may even protect this task from being accidentally interrupted by your users: 
     //  in fact it is not their responsibility 
     // 
     @Override 
     public boolean cancel(boolean mayInterruptIfRunning) { 
      return false; 
     } 
    }; 

    Executor executor = Executors.newCachedThreadPool(); 

    // 3. execute both 
    executor.execute(readLine); 
    executor.execute(timedReadLine); 

    // 4. ...and here comes one of your users who can wait for only a second 
    try { 
     String answer = timedReadLine.get(1, TimeUnit.SECONDS); 
     // 
     // finally user got his (her) answer 
     // 
    } catch (InterruptedException e) { 
     // 
     // someone interrupted this thread while it was blocked in timedReadLine.get(1, TimeUnit.SECONDS) 
     // 
    } catch (ExecutionException e) { 
     // 
     // e.getCause() probably is an instance of IOException due to I/O failure 
     // 
    } catch (TimeoutException e) { 
     // 
     // it wasn't possible to accomplish socketIn.readLine() in 1 second 
     // 
    }