JavaEE6:使用@Asynchronous加速Web应用程序.什么时候?

Tha*_*ham 6 java jsf asynchronous java-ee ejb-3.0

我真的想滥用@Asynchronous来加速我的Web应用程序,因此我想更多地了解这一点,以避免错误地使用此注释.

所以我知道这个带注释的方法中的业务逻辑将在一个单独的线程中处理,因此用户不必等待.所以我有两种持久化数据的方法

public void persist(Object object) {
    em.persist(object);
}

@Asynchronous
public void asynPersist(Object object) {
    em.persist(object);
}
Run Code Online (Sandbox Code Playgroud)

所以我有几个场景,我想问一下这些场景中哪一个不行

1. B is not depend on A 
A a = new A();
asynPersist(a); //Is it risky to `asynPersist(a) here?`
B b = new B();
persist(b);   
//Cant asynPersist(B) here because I need the `b` to immediately
//reflect to the view, or should I `asynPersist(b)` as well?

2. Same as first scenario but B now depend on A. Should I `asynPersist(a)`

3. A and B are not related
A a = new A();
persist(a); //Since I need `a` content to reflect on the view 
B b = new B();
asynPersist(b); //If I dont need content of b to immediately reflect on the view. Can I use asyn here?
Run Code Online (Sandbox Code Playgroud)

编辑:嗨@arjan,非常感谢你的帖子,这是另一个场景,我想问你的专业知识.如果我的案子对你没有任何意义,请告诉我.

4. Assume User has an attribute call `count` type `int`
User user = null;
public void incVote(){
   user = userDAO.getUserById(userId);
   user.setCount(u.getCount() + 1);
   userDAO.merge(user);
}
public User getUser(){   //Accessor method of user
   return user;
}
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,如果我的方法getUserById使用@Asynchronous,那么该行将u.setCount(u.getCount() + 1);阻塞直到u返回结果,是否正确?所以在这种情况下,使用@Asynchronous是没用的,对吗?

如果方法merge(将所有更改合并u回数据库)使用@Asynchronous,如果在我的JSF页面中,我有类似的东西

<p:commandButton value="Increment" actionListener="#{myBean.incVote}" update="cnt"/>
<h:outputText id="cnt" value="#{myBean.user.count}" />
Run Code Online (Sandbox Code Playgroud)

因此按钮将调用方法incVote(),然后发送和ajaxical请求告诉outputText更新自己.这会创建竞争条件(还记得我们使合并异步)吗?因此,当按钮告诉outputText更新自身时,它会调用访问器方法getUser(),行return user;块是否会等待异步userDAO.merge(user),或者这里可能存在竞争条件(该计数可能无法显示正确的结果),因此不建议这样做所以?

Arj*_*jms 10

有很多地方可以利用@Asynchronous.使用此批注,您可以按照Java EE规范的预期编写应用程序; 不要使用显式多线程,而是让托管线程池完成工作.

首先,您可以将其用于"一劳永逸"的行动.例如,向用户发送电子邮件可以在@Asynchronous注释方法中完成.用户无需等待代码联系邮件服务器,协商协议等.让主要请求处理线程等待这一点是浪费每个人的时间.

同样,当用户登录到您的应用程序并再次注销时,您可能会执行一些审核日志记录.这两个持久化动作都是放入异步方法的完美候选者.让用户等待这样的后端管理是没有意义的.

然后,有一类情况需要获取无法(轻松)使用单个查询获取的多个数据项.例如,我经常看到以下应用:

User user = userDAO.getByID(userID);
Invoice invoice = invoiceDAO.getByUserID(userID);
PaymentHistory paymentHistory = paymentDAO.getHistoryByuserID(userID);
List<Order> orders = orderDAO.getOpenOrdersByUserID(userID);
Run Code Online (Sandbox Code Playgroud)

如果按原样执行此操作,您的代码将首先进入数据库并等待提取用户.它闲置在中间.然后它将获取发票并再次等待.等等

这可以通过异步执行这些单独调用来加速:

Future<User> futureUser = userDAO.getByID(userID);
Future<Invoice> futureInvoice = invoiceDAO.getByUserID(userID);
Future<PaymentHistory> futurePaymentHistory = paymentDAO.getHistoryByuserID(userID);
Future<List<Order>> futureOrders = orderDAO.getOpenOrdersByUserID(userID);
Run Code Online (Sandbox Code Playgroud)

一旦您确实需要其中一个对象,代码将自动阻止,如果结果还没有.这允许您重叠提取单个项目,甚至与提取重叠其他处理.例如,您的JSF生命周期可能已经经历了几个阶段,直到您确实需要任何这些对象.

多线程编程很难调试的通常建议并不适用于此.你没有在线程之间进行任何明确的沟通,你甚至没有自己创建任何线程(这是这个历史建议所基于的两个主要问题).

对于以下情况,使用异步执行将是无用的:

Future<user> futureUser = userDAO.getUserById(userId);
User user = futureUser.get(); // block happens here
user.setCount(user.getCount() + 1);
Run Code Online (Sandbox Code Playgroud)

如果您异步执行某些操作并在此后等待结果,则净效果是顺序调用.

该行将返回用户; 阻止等待异步userDAO.merge(用户)

我担心你还没完全搞定它.return语句不知道在另一个上下文中正在处理的实例正在进行的任何操作.这不是Java的工作原理.

在我之前的示例中,该getUserByID方法返回了Future.代码会自动阻止get()操作.

所以如果你有类似的东西:

public class SomeBean {

   Future<User> futureUser;


   public String doStuff() {
      futureUser = dao.getByID(someID);
      return "";
   }

   public getUser() {
      return futureUser.get(); // blocks in case result is not there
   }

}
Run Code Online (Sandbox Code Playgroud)

然后,如果按钮触发AJAX请求并且outputText呈现自身具有绑定someBean.user,则没有竞争条件.如果dao已经做了它的事情,futureUser将立即返回User类型的实例.否则它将自动阻止,直到User实例可用.

关于merge()在您的示例中执行异步操作; 这可能会遇到竞争条件.如果您的bean在视图范围内并且用户在原始合并完成之前再次快速按下按钮(例如,可能第一次双击),则在第一次合并调用仍然持久的同一实例上可能会发生增量.

在这种情况下,您必须首先克隆User对象,然后再将其发送到异步合并操作.

我开始回答的简单示例非常省略,因为它们是关于执行隔离操作或者使用不可变类型(userID,假设它是int或String)作为输入进行读取.

一旦你开始将可变数据传递给异步方法,你就必须绝对确定之后没有对该数据进行任何变异,否则坚持使用简单规则来传递不可变数据.