Предположим, что у нас есть класс под названием AccountService, который управляет состоянием учетных записей.
AccountService определяется как
interface AccountService{
public void debit(account);
public void credit(account);
public void transfer(Account account, Account account1);
}
Учитывая это определение, что является лучшим способом реализовать передачу () так, чтобы можно было гарантировать, что передача является атомарной операцией.
Я интересуюсь ответами, что ссылочный код Java 1.4, а также ответы, которые могли бы использовать ресурсы от java.util.concurrent в Java 5
Синхронизируйте оба объекта Account
и выполните перенос. Убедитесь, что синхронизация всегда выполняется в одном и том же порядке. Для этого сделайте Account
реализацией Comparable
, отсортируйте оба счета и синхронизируйте в этом порядке.
Если вы не упорядочите счета, то возникнет вероятность тупика, если один поток будет переходить от A к B, а другой - от B к A.
Этот точный пример обсуждается на странице 207 книги Java Concurrency in Practice, крайне важной для всех, кто занимается многопоточной разработкой на Java. Код примера доступен на сайте издательства:
Не могли бы вы избежать синхронизации, используя AtomicReference
для баланса аккаунта вместе с get ()
и set ()
?
Если вы можете гарантировать, что все обращения осуществляются через метод передачи, то, вероятно, самый простой подход - просто сделать передачу синхронизированным методом. Это будет потокобезопасным, потому что это гарантирует, что только один поток будет запускать метод передачи одновременно.
Если другие методы также могут получить доступ к AccountService, тогда вы можете решить, чтобы все они использовали одну глобальную блокировку. Самый простой способ сделать это - заключить весь код, который обращается к AccountService, блоком synchronized (X) {...}, где X - это некоторый экземпляр общего / одноэлементного объекта (который может быть самим экземпляром AccountService). Это будет потокобезопасным, потому что только один поток будет обращаться к AccountService одновременно, даже если они находятся в разных методах.
Если этого по-прежнему недостаточно, тогда вам придется использовать более сложные подходы к блокировке. Один из распространенных подходов - индивидуальная блокировка учетных записей перед их изменением ... но тогда вы должны быть очень осторожны, чтобы принимать блокировки в последовательном порядке (например, по идентификатору учетной записи), иначе вы столкнетесь с тупиками.
Наконец, если AccountService является удаленной службой, тогда вы попадаете на территорию распределенных блокировок .... если у вас нет докторской степени в области компьютерных наук и у вас есть годы, которые нужно потратить на исследования, вам, вероятно, следует избегать туда.
Классический пример, очень хорошо объясненный здесь - http://www.javaworld.com/javaworld/jw-10-2001/jw-1012-deadlock.html?page=4
Возможно, вам потребуется полная поддержка транзакций (если, конечно, это настоящее приложение).
Сложность решения практически не зависит от вашего окружения. Подробно опишите вашу систему, и мы постараемся вам помочь (какое приложение? Оно использует веб-сервер? Какой веб-сервер? Что используется для хранения данных? И т. Д.)