好想虐@Asynchronous
为了加速我的 Web 应用程序,因此我想更多地了解这一点,以避免错误地使用此注释。
所以我知道这个带注释的方法中的业务逻辑将在一个单独的线程中处理,所以用户不必等待。所以我有两种方法可以持久化数据
public void persist(Object object) {
em.persist(object);
}
@Asynchronous
public void asynPersist(Object object) {
em.persist(object);
}
所以我有几个场景我想问一下这些场景中的哪一个是不行的
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?
编辑:嗨@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;
}
如果我理解正确,如果我的方法
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}" />
所以按钮将调用方法
incVote()
,然后发送ajaxical请求告诉outputText
自我更新。这会产生竞争条件吗(还记得我们使合并异步)吗?所以当按钮告诉outputText
为了更新自身,它调用访问器方法 getUser()
, 请问行return user;
阻塞等待异步 userDAO.merge(user)
,或者这里可能存在竞争条件(该计数可能不会显示正确的结果),因此不建议这样做?
最佳答案
有很多地方可以利用@Asynchronous。使用此注释,您可以按照 Java EE 规范的意图编写应用程序;不要使用显式多线程,而是让托管线程池完成工作。
首先,您可以将其用于“即发即弃”的操作。例如。可以在@Asynchronous 注释方法中向用户发送电子邮件。用户不需要等待你的代码联系邮件服务器、协商协议(protocol)等。让主请求处理线程等待这是在浪费大家的时间。
同样,当用户登录到您的应用程序并再次注销时,您可能会进行一些审计日志记录。这两个持久操作都是放入异步方法的完美候选者。让用户等待这样的后台管理是没有意义的。
然后有一类情况,您需要获取无法(轻松)使用单个查询获取的多个数据项。例如,我经常看到这样的应用:
User user = userDAO.getByID(userID);
Invoice invoice = invoiceDAO.getByUserID(userID);
PaymentHistory paymentHistory = paymentDAO.getHistoryByuserID(userID);
List<Order> orders = orderDAO.getOpenOrdersByUserID(userID);
如果您按原样执行此操作,您的代码将首先进入数据库并等待获取用户。它处于闲置状态。然后它会去取发票并再次等待。等等等等。
这可以通过异步执行这些单独的调用来加速:
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);
一旦您真正需要这些对象之一,如果结果尚不存在,代码将自动阻塞。这允许您重叠单个项目的提取,甚至与提取重叠其他处理。例如,您的 JSF 生命周期可能已经经历了几个阶段,直到您真正需要这些对象中的任何一个。
多线程编程难以调试的常见建议在这里并不适用。您没有在线程之间进行任何显式通信,您甚至没有自己创建任何线程(这是历史建议所基于的两个主要问题)。
对于以下情况,使用异步执行将毫无用处:
Future<user> futureUser = userDAO.getUserById(userId);
User user = futureUser.get(); // block happens here
user.setCount(user.getCount() + 1);
如果您异步执行某些操作,然后立即等待结果,则最终效果是顺序调用。
will the line return user; block to wait for the asynchronous userDAO.merge(user)
恐怕你还没有完全明白。 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
}
}
然后,如果按钮触发了 AJAX 请求,并且 outputText 将自身呈现为与
someBean.user
的绑定(bind)。 ,那么就没有竞争条件。如果 dao 已经做了它的事情,futureUser 将立即返回一个 User 类型的实例。否则它会自动阻塞,直到 User 实例可用。关于做
merge()
在您的示例中异步操作;这可能会遇到竞争条件。如果您的 bean 在 View 范围内并且用户在原始合并完成之前再次快速按下按钮(例如,可能第一次双击),则在第一次合并调用仍在持续的同一实例上可能会发生增量。在这种情况下,您必须先克隆 User 对象,然后再将其发送到异步合并操作。
我开始这个答案的简单示例非常节省,因为它们是关于执行隔离操作或使用不可变类型(用户 ID,假设它是 int 或 String)作为输入进行读取。
一旦您开始将可变数据传递到异步方法中,您就必须绝对确定之后没有对该数据进行任何更改,否则坚持只传递不可变数据的简单规则。
关于JavaEE6 : using @Asynchronous to speed up the web application. 什么时候?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5264718/