【问题标题】:Java Socket read operation not timing out in threadJava Socket读取操作未在线程中超时
【发布时间】:2023-04-01 06:49:01
【问题描述】:

我惊讶地发现 Java 并发超时并没有停止线程中阻塞的套接字读取操作。

我使用 Selenium RemoteWebDriver 来模拟负载测试场景。我已将 Selenium 命令的执行包装在 Callable 中,并将 get() 方法与 timeout 参数一起使用。当执行的并发线程少于 3 个时,它可以完美运行,但当有 4 个或更多并发线程时,情况会恶化。一些线程在套接字读取时卡住,并且卡住的时间超过了Callable 上的超时设置。

我在网上做了一些研究,根本原因是 Selenium 代码中硬编码的 3 小时套接字超时。曾经有一个黑客可以使用反射覆盖设置,但是对于最新版本,我认为它不再是可破解的了。

我想知道是否有办法停止被外部 IO 阻塞的线程,因为我不想更改 Selenium 代码并最终不得不维护我自己的 Selenium 版本。

这是我在代码中处理线程超时的方式:

ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Long> future = null;
try {
    future = executorService.submit(new Callable<Long>() {
         @Override
         public Long call() throws Exception {
             return commandProcessor.process(row);
         }
     });   
timeTaken = future.get(currentTimeout, TimeUnit.MILLISECONDS);
executorService.shutdown();
} catch (Exception e) {
    log.error(e.getMessage(), e);
    // cancel the task
    if (future != null && !future.isDone()) {
         future.cancel(true);
    }
    executorService.shutdownNow();
}

并且由于它随机超时失败,我什至创建了另一个守护线程来监视该线程并从外部将其关闭,但是当套接字读取阻塞时它仍然无法终止线程。

在try块中:

TimeoutDaemon timeoutDaemon = new TimeoutDaemon(future, executorService, timeStarted);

// put a daemon on the main execution thread so it behaves
ExecutorService daemonExecutorService = Executors.newSingleThreadExecutor();
daemonExecutorService.submit(timeoutDaemon);

TimeoutDaemon 类:

private class TimeoutDaemon implements Runnable {

    private Future<?> future;
    private long timeStarted;
    private ExecutorService taskExecutorService;

    private TimeoutDaemon(Future<?> future, ExecutorService taskExecutorService, long timeStarted) {
        this.future = future;
        this.timeStarted = timeStarted;
        this.taskExecutorService = taskExecutorService;
    }

    @Override
    public void run() {
        boolean running = true;
        while (running) {
            long currentTime = System.currentTimeMillis();
            if (currentTime - timeStarted > currentTimeout + 1000) {
                running = false;
                if (!future.isDone()) {
                    String message = "Command execution is taking longer (%d ms) than the current timeout setting %d. Canceling the execution.";
                    message = String.format(message, currentTime - timeStarted, currentTimeout);
                    taskExecutorService.shutdownNow();
                }
            } else {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    log.error("Timeout Daemon interrupted. Test may be stuck. Close stuck browser windows if any.", e);
                }
            }
        }
    }
}

【问题讨论】:

    标签: java multithreading sockets selenium concurrency


    【解决方案1】:

    我知道的唯一方法是关闭套接字。 你是对的,令人失望的是 api 不允许中断或其他东西。 另见Interrupt/stop thread with socket I/O blocking operation

    【讨论】:

      【解决方案2】:

      来自 API 规范:

      列出shutdownNow() 尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表。 此方法不等待主动执行的任务终止。使用 awaitTermination 来做到这一点。

      除了尽力停止处理正在执行的任务之外,没有任何保证。例如,典型的实现将通过 Thread.interrupt() 取消,因此任何未能响应中断的任务都可能永远不会终止。

      您不能中断 Socket.read() 操作。

      您可以查看提供 InterruptibleChannel 的 NIO 包。可以通过调用其 close 方法来中断 InterruptibleChannel 上的读写操作。

      来自 API:

      如果线程在可中断通道上的 I/O 操作中被阻塞,则另一个线程可能会调用通道的关闭方法。这将导致被阻塞的线程接收到 AsynchronousCloseException。

      【讨论】:

        猜你喜欢
        • 2011-04-04
        • 1970-01-01
        • 2012-01-18
        • 2017-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-16
        • 1970-01-01
        相关资源
        最近更新 更多