JavaEE6: использование @Asynchronous для ускорения работы веб-приложения.Когда? - PullRequest
6 голосов
/ 10 марта 2011

Я действительно хочу злоупотребить @Asynchronous для ускорения работы моего веб-приложения, поэтому я хочу понять это немного больше, чтобы избежать неправильного использования этой аннотации.

Так что я знаю, что бизнес-логика внутри этого аннотированного метода будет обрабатываться в отдельном потоке, поэтому пользователю не придется ждать. Итак, у меня есть два метода, которые сохраняют данные

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), или здесь может возникнуть условие гонки (этот счет может не отображается правильный результат) и, следовательно, не рекомендует делать это?

Ответы [ 2 ]

10 голосов
/ 12 марта 2011

Есть довольно много мест, где вы можете воспользоваться @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);

Если вы выполните это как есть, ваш код сначала пойдет в БД и будет ждать выборки пользователя.Он сидит без дела между ними.Затем он отправится за счетом и снова ждет.И т. Д.

Это можно ускорить, выполнив эти отдельные вызовы асинхронно:

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);

Если вы делаете что-то асинхронно и сразу после этого ожидаете результат, чистый эффект - это последовательный вызов.

вернет ли пользователь строку;блок для ожидания асинхронного 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, тогда нет условий гонки.Если дао уже сделал свое дело, futureUser немедленно вернет экземпляр типа User.В противном случае он будет автоматически блокироваться до тех пор, пока экземпляр User не станет доступен.

Относительно выполнения асинхронной операции merge() в вашем примере;это может столкнуться с условиями гонки.Если ваш компонент находится в области видимости, и пользователь быстро нажимает кнопку еще раз (например, возможно, дважды щелкнув первый раз), прежде чем будет выполнено первоначальное слияние, приращение может произойти в том же экземпляре, в котором первый вызов слияния все еще сохраняется.

В этом случае вам необходимо сначала клонировать объект User перед отправкой его в асинхронную операцию слияния.

Простые примеры, с которых я начал этот ответ, довольно безопасны, поскольку они предназначены для выполнения изолированногодействие или выполнение чтения с неизменным типом (userID, предположим, что это int или String) в качестве входных данных.

Как только вы начнете передавать изменяемые данные в асинхронные методы, вы должны быть абсолютно уверены, что впоследствии эти данные не будут изменены, иначе придерживайтесь простого правила, чтобы передавать только неизменяемые данные.

1 голос
/ 10 марта 2011

Вы не должны использовать асинхронность таким образом, если какой-либо процесс, следующий за асинхронной частью, зависит от результата. Если вы сохраните данные, которые нужны более позднему потоку, у вас будет состояние гонки, которое будет плохой идеей.

Я думаю, тебе следует сделать шаг назад, прежде чем идти по этому пути. Напишите ваше приложение в соответствии с рекомендациями Java EE: однопоточное, с потоками, обрабатываемыми контейнером. Профилируйте свое приложение и узнайте, где время тратится. Внесите изменения, выполните повторную настройку и посмотрите, имел ли он желаемый эффект.

Многопоточные приложения сложно писать и отлаживать. Не делайте этого, если у вас нет веских причин и надежных данных для поддержки ваших изменений.

...