如何在多线程环境中更好地使用ExecutorService? [英] How to better use ExecutorService in multithreading environment?

查看:82
本文介绍了如何在多线程环境中更好地使用ExecutorService?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要创建一个库,其中将包含同步和异步方法.

I need to make a library in which I will have synchronous and asynchronous methods in it.

  • executeSynchronous()-等待直到得到结果,然后返回结果.
  • executeAsynchronous()-立即返回一个Future,如果需要,可以在完成其他操作之后进行处理.
  • executeSynchronous() - waits until I have a result, returns the result.
  • executeAsynchronous() - returns a Future immediately which can be processed after other things are done, if needed.

我的图书馆的核心逻辑

客户将使用我们的库,他们将通过传递DataKey构建器对象来调用它.然后,我们将使用该DataKey对象构造一个URL,并通过执行该URL对该URL进行HTTP客户端调用,然后将响应作为JSON字符串返回给我们,然后将该JSON字符串按原样发送回我们的客户通过创建DataResponse对象.有些客户会调用executeSynchronous(),有些可能会调用executeAsynchronous(),所以这就是为什么我需要在库中分别提供两个方法的原因.

The customer will use our library and they will call it by passing DataKey builder object. We will then construct a URL by using that DataKey object and make a HTTP client call to that URL by executing it and after we get the response back as a JSON String, we will send that JSON String back to our customer as it is by creating DataResponse object. Some customer will call executeSynchronous() and some might call executeAsynchronous() so that's why I need to provide two method separately in my library.

接口:

public interface Client {

    // for synchronous
    public DataResponse executeSynchronous(DataKey key);

    // for asynchronous
    public Future<DataResponse> executeAsynchronous(DataKey key);
}

然后我有实现以上Client接口的DataClient:

And then I have my DataClient which implements the above Client interface:

public class DataClient implements Client {

    private RestTemplate restTemplate = new RestTemplate();
    // do I need to have all threads as non-daemon or I can have daemon thread for my use case?
    private ExecutorService executor = Executors.newFixedThreadPool(10);

    // for synchronous call
    @Override
    public DataResponse executeSynchronous(DataKey key) {
        DataResponse dataResponse = null;
        Future<DataResponse> future = null;

        try {
            future = executeAsynchronous(key);
            dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
        } catch (TimeoutException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, key);
            dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
            future.cancel(true); // terminating tasks that have timed out
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    //for asynchronous call
    @Override
    public Future<DataResponse> executeAsynchronous(DataKey key) {
        Future<DataResponse> future = null;

        try {
            Task task = new Task(key, restTemplate);
            future = executor.submit(task); 
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
        }

        return future;
    }
}

将执行实际任务的简单类:

Simple class which will perform the actual task:

public class Task implements Callable<DataResponse> {

    private DataKey key;
    private RestTemplate restTemplate;

    public Task(DataKey key, RestTemplate restTemplate) {
        this.key = key;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() {
        DataResponse dataResponse = null;
        String response = null;

        try {
            String url = createURL();
            response = restTemplate.getForObject(url, String.class);

            // it is a successful response
            dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
        } catch (RestClientException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
            dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
        } catch (Exception ex) { // should I catch RuntimeException or just Exception here?
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    // create a URL by using key object
    private String createURL() {
        String url = somecode;
        return url;
    }
}

我对上述解决方案没什么疑问-

I have few questions on my above solution -

  • 在上述用例中应该使用守护程序线程还是非守护程序线程?
  • 此外,我正在终止超时的任务,以便长时间不占用我有限的10个线程之一.这样看起来对吗?
  • 在我的call()方法中,我正在捕获Exception.我应该在那儿赶上RuntimeException吗?如果我捕获Exception或RuntimeException有什么区别?
  • Should I use daemon or non daemon threads for my above use case?
  • Also, I am terminating the tasks that have timed out so that it doesn't occupy one of my limited 10 threads for a long time. Does that look right the way I am doing it?
  • In my call() method, I am catching Exception. Should I catch RuntimeException there? What is the difference if I catch Exception or RuntimeException?

当我开始研究此解决方案时,我并没有终止超时的任务.我正在向客户端报告超时,但是任务继续在线程池中运行(很长时间可能占用了我有限的10个线程之一).因此,我在网上进行了一些研究,发现可以通过在将来使用cancel来取消超时的任务,如下所示-

When I started working on this solution, I was not terminating the tasks that have timed out. I was reporting the timeout to the client, but the task continues to run in the thread pool (potentially occupying one of my limited 10 threads for a long time). So I did some research online and I found that I can cancel my tasks those have timed out by using cancel on future as shown below -

future.cancel(true);

但是我想确定的是,它看起来像我在executeSynchronous方法中取消正在超时的任务的方式一样正确吗?

But I wanted to make sure, does it look right the way I am doing in my executeSynchronous method to cancel the tasks that have got timedout?

由于我正在调用Future上的cancel(),如果任务仍在队列中,它将阻止其运行,所以我不确定我在做什么是否正确?正确的做法是什么?

Since I am calling cancel() on the Future which will stop it from running if tasks is still in the queue so I am not sure what I am doing is right or not? What is the right approach to do this?

如果有更好的方法,那么有人可以提供示例吗?

If there is any better way, then can anyone provide an example for that?

我们是否应该总是终止已经超时的任务?如果我们不这样做,那我会有什么影响?

Should we always be terminating the tasks that have got timed out? If we don't do that then what might be the impact I will have?

推荐答案

在上述用例中,应该使用守护程序线程还是非守护程序线程?

Should I use daemon or non daemon threads for my above use case?

这取决于.但是在这种情况下,我希望使用守护程序线程,因为使用客户端很方便,因为它允许进程退出.

It depends. But in this case I would prefer daemon threads because it is convenient to use clients which allows the process to exit.

这看起来像我的做法吗?

Does that look right the way I am doing it?

不,不是.中断IO任务非常困难.也尝试在RestTemplate中设置超时.在这种情况下取​​消未来似乎毫无意义.

No, it doesn't. Interrupting of an IO task is pretty hard. Try to set timeout in RestTemplate too. Canceling of the future in this case seems meaningless.

如果我捕获Exception或RuntimeException,有什么区别?

What is the difference if I catch Exception or RuntimeException?

如果在try块中没有检查异常,则没有区别:)只是因为在这种情况下只有RuntimeExceptions可能.

If you don't have checked exceptions in try blocks there is no difference :) Just because only RuntimeExceptions possible in that case.

还有一个重要的注意事项:将同步调用实现为异步+等待是一个坏主意.它是没有意义的,并且每次调用消耗线程池中的一个线程.只需创建Task实例并在当前线程中调用它即可!

And one more important note: implementing sync call as async + waiting is bad idea. It is meaningless and consumes one thread from the thread pool per a call. Just create instance of the Task and call it in current thread!

这篇关于如何在多线程环境中更好地使用ExecutorService?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆