【问题标题】:executor pool in a java serverjava服务器中的执行器池
【发布时间】:2014-07-09 14:51:45
【问题描述】:

我有一个 java 服务器,它正在处理客户端请求的网页中的图像。

现在处理这些图像需要时间和内存,并且进入处理图像的 n 个线程会挂起服务器。

现在为了避免处理图像的代码的情况,我将它们放在一个 Runnable 类中并从执行器池中调用它们。

我的问题是,下面的实现是否是在服务器中执行 Executor 池的正确方法。

Runnable 类是 -

public class MyRunnable implements Runnable {
     private final String id;

    MyRunnable(String tid) {
        this.id = tid;
    }

    @Override
    public void run() {
        NewAlbumImage nai = new NewAlbumImage();                
        nai.save_image(id,false);        
    }
} 

运行 Runnable 类的代码如下 -

newa.NewClass newca = new newa.NewClass();
Runnable mr = new MyRunnable(id);
newca.executor.execute(mr);

NewClass 类有一个静态执行器变量 -

static ExecutorService executor;

我在 web 应用程序启动时初始化 executor 变量,并在 web 应用程序关闭时销毁它 -

public class AppNameServletContextListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
    System.out.println("Initializing Executor Pool");
    NewClass nc = new NewClass();
    nc.executor = Executors.newFixedThreadPool(10);


}

@Override
public void contextDestroyed(ServletContextEvent sce) {
    NewClass nc = new NewClass();
    nc.executor.shutdown();
    try {
        nc.executor.awaitTermination( 10L, TimeUnit.MINUTES);
    } catch (InterruptedException ex) {
        System.out.println("Executor Pool await Termination exception");
    }
}   
}

【问题讨论】:

    标签: java multithreading tomcat executorservice


    【解决方案1】:

    有些事情我会考虑。

    首先,您的执行者是静态的。我会使用某种依赖注入框架在需要的地方正确地注入你的执行程序。或者将其作为属性添加到您的 servlet 上下文中,并在 servlet 初始化时自行注入。

    使用 ServletContext 创建/关闭:

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        context.setAttribute("executor", Executors.newCachedThreadPool());
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        ExecutorService executor = (ExecutorService)context.getAttribute("executor");
        executor.shutdown();
    }   
    

    其次,您使用的是固定线程池。这可能是一个瓶颈(但也许是故意节省 CPU 的?)。如果不能同时调整超过 10 张图像的大小,那就没问题了。但是如果处理它们的速率比请求进来的速率慢,你会发现一个固定的线程池是行不通的。事实上,如果您的应用程序加入调整大小任务以将响应返回给用户,那么您已将任何给定时刻的可能请求数限制为定义的线程数。尝试使用缓存线程池。线程是按需创建的,并且会因不活动而死亡。

    Executors.newCachedThreadPool();
    

    最后,我认为这更重要,您的应用程序将如何扩展?您拥有的用户越多,将在您的网络服务器上处理的图像就越多。您的响应时间将很快下降。同时尝试调整图像大小的只有三个用户可以使用 100% 的 CPU。我建议将您的图像大小调整到另一台功能更强大的机器上。

    【讨论】:

    • 但是如果它们被处理的速度比请求进来的速度慢,你会发现一个固定的线程池是行不通的。我知道如果发生这种情况,页面将等到执行程序尚未完成处理图像。
    【解决方案2】:

    你提到了

    NewClass 类有一个静态执行器变量—— 静态ExecutorService执行器;

    我建议使用

    NewClass.executor.execute(yourRunnableInstance);
    
    NewClass.executor.shutdown();
    

    这很容易看出executor是一个静态字段,你不需要创建多个NewClass实例。

    其余的代码看起来不错。

    【讨论】:

    • 如果我关闭执行器,那么我在哪里初始化它?
    • 我说静态字段最好是通过类名来调用,而不是创建一个新的类实例,这会让人困惑:)NewClass.executor = Executors.newFixedThreadPool(10);newFixedThreadPool()是一个静态工厂方法,它会为您初始化一个新的ExecutorService 实例。
    • 如果 10 个线程(页面请求)初始化同一个静态变量并执行和关闭会不会导致问题?
    • 会的。我的观点是使用静态字段时,使用类名调用它,无论是新实例调用字段还是类名,线程问题都不会改变。
    猜你喜欢
    • 1970-01-01
    • 2014-08-27
    • 2017-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-13
    • 1970-01-01
    • 2021-12-20
    相关资源
    最近更新 更多