Java EE 规范和多线程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3212255/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:27:45  来源:igfitidea点击:

Java EE specification and multi threading

javamultithreadingjakarta-ee

提问by Sid

I am writing a Java EE application using Struts and Spring. In one of the operations there is heavy database processing, and hence performance issues. What I want to know is can I use multithreading here? I think the Java EE specification does not allow custom threads to be created apart from those created by Server (I use Weblogic). Please guide me through this.

我正在使用 Struts 和 Spring 编写 Java EE 应用程序。其中一项操作有繁重的数据库处理,因此存在性能问题。我想知道的是我可以在这里使用多线程吗?我认为 Java EE 规范不允许创建除服务器创建的线程之外的自定义线程(我使用 Weblogic)。请指导我完成这个。

采纳答案by ewernli

This question pops up once in a while.

这个问题偶尔会出现。

As per the spec it's not authorized. The best page to look at is this one: Q/A: J2EE Restrictions

根据规范,它未经授权。最好看的页面是这个:Q/A: J2EE Restrictions

That said, there are ways to spawn threads, especiall in Weblogic with the WorkManager.

也就是说,有很多方法可以生成线程,特别是在 Weblogic 中使用WorkManager.

See these questions:

看到这些问题:

The fact that the first one targets EJB shouldn't matter that much, and the last one about access to file system is about general restrictions.

第一个针对 EJB 的事实应该无关紧要,而最后一个关于访问文件系统的事实与一般限制有关。

Hope it helps.

希望能帮助到你。

回答by J?rn Horstmann

These restrictions are in place mostly because Java EE and EJB want to support transparent clustering. For example one server of a cluster should not modify files because these changes can not be easily mirrored to other servers. For threads there is the question if there should be one thread per cluster or per server. These threads also can not be easily monitored by the application server.

这些限制主要是因为 Java EE 和 EJB 想要支持透明集群。例如,集群的一台服务器不应修改文件,因为这些更改无法轻松镜像到其他服务器。对于线程,存在每个集群或每个服务器是否应该有一个线程的问题。这些线程也不容易被应用服务器监控。

That said, it should be possible to create threads, socket connections or access the filesystem in a Java EE server just like in a normal application.

也就是说,应该可以像在普通应用程序中一样在 Java EE 服务器中创建线程、套接字连接或访问文件系统。

回答by Chris Ritchie

The recommended way to create threads in a Java EE environment, is with the Concurrency Utils API, which is part of the EE7 specification.

在 Java EE 环境中创建线程的推荐方法是使用 Concurrency Utils API,它是 EE7 规范的一部分。

By using this API your new thread will be created, and managed by the container, guaranteeing that all EE services are available to your thread (eg security, transactions).

通过使用这个 API,你的新线程将被创建,并由容器管理,保证所有 EE 服务对你的线程可用(例如安全、事务)。

The examples below are taken from my own site hereand here

下面的例子取自我自己的网站herehere

Using a ManagedExecutorService

使用 ManagedExecutorService

To create a new thread using a ManagedExecutorService, first create a task object that implements Callable. Within the call() method we will define the work that we want carried out in a separate thread.

要使用 ManagedExecutorService 创建新线程,首先创建一个实现 Callable 的任务对象。在 call() 方法中,我们将定义要在单独线程中执行的工作。

public class ReportTask implements Callable<Report> {

    Logger logger = Logger.getLogger(getClass().getSimpleName());

    public Report call() {
        try {
            Thread.sleep(3000);
        catch (InterruptedException e) {
            logger.log(Level.SEVERE, "Thread interrupted", e);
        }
        return new Report();
    }
}

Then we need to invoke the task by passing it though to the submit() method of the ManagedExecutorService.

然后我们需要通过将它传递给 ManagedExecutorService 的 submit() 方法来调用任务。

@Stateless
public class ReportBean {

    @Resource
    private ManagedExecutorService executorService;

    public void runReports() {
        ReportTask reportTask = new ReportTask();
        Future<Report> future = executorService.submit(reportTask);
    }
}

Using a ManagedThreadFactory

使用 ManagedThreadFactory

First create a Runnable task which will define what work is to be done in the background.

首先创建一个 Runnable 任务,它将定义要在后台完成的工作。

public class ReportTask implements Runnable {

    Logger logger = Logger.getLogger(getClass().getSimpleName());

    public void run() {
        try {
            //do your background task
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            logger.log(Level.SEVERE, "Thread interrupted", e);
        }
    }
}

To get a container managed thread, we simply ask the ManagedThreadFactory for a new thread, and pass it our Runnable instance. To start the thread we call start().

要获得容器管理的线程,我们只需向 ManagedThreadFactory 请求一个新线程,并将其传递给我们的 Runnable 实例。要启动线程,我们调用 start()。

@Stateless
public class ReportBean {

    @Resource
    private ManagedThreadFactory threadFactory;

    public void runReports() {
        ReportTask reportTask = new ReportTask();
        Thread thread = threadFactory.newThread(reportTask);
        thread.start();
    }
}

回答by Leonardo Savio

If you need to run several Threads here is a suggestion(or alternative way) with simple control's pool:

如果您需要运行多个线程,这里是一个带有简单控件池的建议(或替代方法):

1 - Pass your context(EJB) as a parameter to your method(rest endpoint, scheduler, default's methods)

1 - 将您的上下文(EJB)作为参数传递给您的方法(其余端点、调度程序、默认方法)

2 - Control the state with complementary scheduler or entity flags 3 - Be careful with the volume of data/processing

2 - 使用补充调度程序或实体标志控制状态 3 - 小心数据/处理量

4 - Recommendations: Metrics, logs, and tests, tests, tests are strongly recommended

4 - 建议:强烈推荐指标、日志和测试、测试、测试

5 - This code is on SpringBoot but was tested in Jboss(with modifications) under EJB Context - Test carefully

5 - 此代码在 SpringBoot 上,但在 EJB 上下文下的 Jboss(经过修改)中进行了测试 - 仔细测试

6 - Use/modify as you wish: (send suggestions/comments)

6 - 根据需要使用/修改:(发送建议/评论)

BaseControlExecutor.java

BaseControlExecutor.java

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


public class BaseControlExecutor {

    private final ScheduledThreadPoolExecutor poolExec = new ScheduledThreadPoolExecutor(2);

    public void execWithTimeout(final Runnable runnable, long timeout,
            TimeUnit timeUnit) throws Exception {
        execWithTimeout(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                runnable.run();
                return null;
            }
        }, timeout, timeUnit);
    }

    public <T> T execWithTimeout(Callable<T> callable, long timeout,    TimeUnit timeUnit) throws Exception {

        final Future<T> future = poolExec.submit(callable);

        try {
            return future.get(timeout, timeUnit);
        } catch (TimeoutException e) {
            future.cancel(true);
            throw e;
        } catch (ExecutionException e) {
            Throwable t = e.getCause();
            if (t instanceof Error) {
                throw (Error) t;
            } else if (t instanceof Exception) {
                throw (Exception) t;
            } else {
                throw new IllegalStateException(t);
            }
        }
    }
}

EndpointControlRest.java

EndpointControlRest.java

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping(value = "/report")
@Api(tags = "Endpoint of Future")
public class EndpointControlRest extends BaseControlExecutor {

    Logger logger = LoggerFactory.getLogger(EndpointControlRest.class);

    //single metric of execution
    protected final AtomicLong counter = new AtomicLong();

    @GetMapping(path = "/withThread", produces = { "application/json" })
    @ApiOperation(value = "Return Hello count.")
    public String greeting() {

        Long countRunner = counter.incrementAndGet();
        String json = ""; //or EJB context to use in Thread - becareful

        new Thread(() -> {

            try {
                execWithTimeout(new Runnable() {
                    @Override
                    public void run() {

                        Instant start = Instant.now();
                        logger.info("Report init - " + countRunner);

                        //generating reports
                        generateBackgroundReport(json);

                        logger.info("Report End - " + countRunner);

                        Instant finish = Instant.now();
                        long timeElapsed = Duration.between(start, finish).toMillis();

                        logger.info("###DEBUG - " + countRunner + " - OK |Time exe: " + timeElapsed);

                    }
                }, 120, TimeUnit.SECONDS);
            } catch (TimeoutException e) {
                logger.info("###DEBUG - " + countRunner + " - Timeout - " + e.getMessage());
            } catch (Exception e) {
                logger.info("###DEBUG - " + countRunner + " - Exception - " + e.getMessage());
            }
        }).start();

        logger.info("####DEBUG - Rest call released");
        return "Hello " + countRunner;
    }

    public String generateBackgroundReport(String json){

        //simulating work
        Long x = 0L;
        for(Long i = 0L; i < 1000000000L; i ++){
            x = i + 1;
        }
        logger.info("####DEBUG -report: " + x);
        return "OK";
    }
}