2011-03-13 45 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 
     // 
    }