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
Java EE specification and multi threading
提问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:
看到这些问题:
- How can an EJB parallelize a long, CPU intensive process?
- Why spawning threads in J2EE container is discouraged?
- J2EE programmers do not write to files
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
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";
}
}