【问题标题】:How can I timeout a function that blocks in Java?如何使在 Java 中阻塞的函数超时?
【发布时间】:2011-03-13 19:59:08
【问题描述】:

基本上,我正在调用 BufferedReader.ReadLine();但是我在一个多线程服务器中,我正在同步树中的一个节点。因此,当调用此 ReadLine 函数时,如果其他人到达该节点,他们就会被锁定。我不知道如何对 ReadLine 在退出线程之前等待响应的时间量进行时间限制。我得到的最接近的是创建一个会休眠 1ms 的新线程,然后检查我设置 ReadLine 的变量是否已更改。所以是这样的:

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 工作,但不能。这是我的答案吗?我将如何实现它?

[EDIT] socketIn 是一个 BufferedReader,应该明确表示抱歉。 此外,客户端通过 telnet 进行连接,但我认为这并不重要。

我在这里做的是一个“名人猜谜游戏”,用户可以在其中将名人添加到树中。所以我需要锁定这个人正在编辑的节点以保证线程安全

【问题讨论】:

  • 在下面查看我的答案 - 如果您在等待输入时锁定,您将被卡住(除非您超时读取,在这种情况下您会很慢)。上面的代码更加危险,因为您正在启动一个新线程并试图让两个线程访问answer 而无需锁定(同步)。

标签: java multithreading synchronization timeout


【解决方案1】:

这是作业吗?这与昨天其他人提出的问题非常接近。如果是这样,它应该有作业标签。

只有当一个线程将修改其他线程可能读取/修改的数据时,您才需要锁定某些东西。

如果您要锁定等待输入的内容,则锁定的范围太广了。

您的流程应该是:

  • 从客户端读取输入(阻塞 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");
}

【讨论】:

    【解决方案2】:

    一切都已经完成了。尝试使用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
            //
        }
    

    【讨论】:

      【解决方案3】:

      我想出了一个解决办法:

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

      感谢您观看,Brian Roach。你的帖子内容丰富,但我不小心遗漏了一些关键信息。以后我会更加小心的。

      【讨论】:

        猜你喜欢
        • 2020-10-07
        • 1970-01-01
        • 1970-01-01
        • 2013-11-16
        • 1970-01-01
        • 2013-12-07
        • 2023-03-31
        • 2019-06-09
        • 1970-01-01
        相关资源
        最近更新 更多