繁体   English   中英

Java套接字读取操作未在线程中超时

[英]Java Socket read operation not timing out in thread

我很惊讶地发现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);
                }
            }
        }
    }
}

我知道的唯一方法是关闭插座。 没错,API不允许中断或其他令人失望的事情。 另请参见具有套接字I / O阻止操作的中断/停止线程

根据API规范:

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

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

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

您可以看一下提供InterruptibleChannel的NIO软件包。 可以通过调用其close方法来中断InterruptibleChannel上的读取和写入操作。

从API:

如果某个线程在可中断通道的I / O操作中被阻塞,则另一个线程可以调用该通道的close方法 这将导致被阻止的线程接收一个AsynchronousCloseException。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM