如何实现具有超时功能的Runnable?

6
我目前有一个相对简单的SSL服务器/客户端项目,涉及标准的Java后端和Android前端。我想尝试在客户端发送数据后从服务器读取响应。然而,我遇到了一些问题。由于read()方法是阻塞的,因此服务器并不总是会响应,所以我必须以某种异步方式尝试获取响应。经过一番摸索之后,我找到了一个可行的解决方案,但它看起来很奇怪,就好像我正在做一些潜在的“危险”事情。 首先我创建了一个新的Runnable和Thread对象:
                    //  "reader" is of type "BufferedReader"             

                    Runnable receiveResponse = new Runnable() {
                        boolean stop = false;
                        @Override
                        public void run() {
                            try {
                                new Timer().schedule(new TimerTask() {
                                    @Override
                                    public void run() {
                                        stop = true;
                                    }
                                }, 5000);
                                char receive;
                                StringBuilder responseBuilder = new StringBuilder();
                                while (!stop && ((receive = (char) reader.read()) != END_OF_STREAM_CHAR)) {
                                    responseBuilder.append(receive);
                                }
                                // If the server responds in time I can continue processing the response etc.
                            } catch (Exception ex) {
                                // Handle exception ...
                            }
                        }
                    };
                    Thread thread = new Thread(receiveResponse);
                    thread.start();

正如您所看到的,我在while循环中使用了一个布尔值"stop"(在5秒钟后设置为true)作为额外的条件。"reader.read()"方法是一种阻塞式的方法,如果我不实现某种类型的线程停止,那么如果服务器没有响应,这个方法将会无限期地阻塞。这就是为什么我尝试使用一个定时器来简单地将"stop"布尔值设置为true。
我知道这不是最好的解决方案,而且在某些时候我会重新设计整个东西,但我仍然想知道我现在做的这件事是否正确。它肯定起作用,但这并不意味着什么。
1个回答

5

您说得对,使用启动另一个线程的方法并不完美。支持这样的代码会很困难。

我建议使用一个线程执行器(Thread Executor)。您的代码将如下所示:

Runnable receiveResponse = new Runnable() {
    ...
};

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(receiveResponse);

try {
    Object result = future.get(5000, TimeUnit.MILLISECONDS);
    System.out.println("Completed successfully");
} catch (InterruptedException e) {
    ...
} catch (ExecutionException e) {
    ...
} catch (TimeoutException e) {
    System.out.println("Timed out. Cancelling the runnable...");
    future.cancel(true);
}

executor.shutdown();

我如何在Runnable内部返回值并在之后使用该返回值? - canbax
@canbax: Runnable 没有返回结果。Future.get() 将返回 null。如果您需要返回结果,请改用 Callable。然后 Future.get() 将返回 Callable 的结果。 - mentallurg
谢谢,我后来看到了 Callable 但它也使执行变得更加复杂。因此,我想我将手动测量执行时间。 - canbax
@canbax:这取决于任务的类型。在某些任务中,使用Runnable返回一个值会更加困难,而使用Callable则非常容易。 - mentallurg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接