2013-03-13 10 views
8

Если у меня есть класс Java определены ниже, который вводится в моем веб-приложение, с помощью инъекции зависимостей:Spring Singleton Потокобезопасность

public AccountDao 
{ 
    private NamedParameterJdbcTemplate njt; 
    private List<Account> accounts; 

    public AccountDao(Datasource ds) 
    { 
     this.njt = new NamedParameterJdbcTemplate(ds); 
     refreshAccounts(); 
    } 

    /*called at creation, and then via API calls to inform service new users have 
    been added to the database by a separate program*/ 
    public void refreshAccounts() 
    { 
     this.accounts = /*call to database to get list of accounts*/ 
    } 

    //called by every request to web service 
    public boolean isActiveAccount(String accountId) 
    { 
     Account a = map.get(accountId); 
     return a == null ? false : a.isActive(); 
    } 
} 

Я обеспокоен безопасностью потоков. Разве Spring Framework не обрабатывает случаи, когда один запрос читается из списка и в настоящее время он обновляется другим? Я использовал блокировку чтения/записи раньше в других приложениях, но я никогда не думал о таком случае, как прежде.

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

Кстати, это является продолжением на вопрос ниже:

Java Memory Storage to Reduce Database Load - Safe?

EDIT:

Так бы код, как это решить эту проблему:

/*called at creation, and then via API calls to inform service new users have 
     been added to the database by a separate program*/ 
     public void refreshAccounts() 
     { 
      //java.util.concurrent.locks.Lock 
      final Lock w = lock.writeLock(); 
      w.lock(); 
      try{ 
       this.accounts = /*call to database to get list of accounts*/ 
      } 
      finally{ 
      w.unlock(); 
      } 
     } 

     //called by every request to web service 
     public boolean isActiveAccount(String accountId) 
     { 
      final Lock r = lock.readLock(); 
      r.lock(); 

      try{ 
       Account a = map.get(accountId); 
      } 
      finally{ 
       r.unlock(); 
      } 
      return a == null ? false : a.isActive(); 
     } 

ответ

13

Весенний каркас не делает ничего под капотом относительно многопоточного поведения одноэлементного компонента. Обязанностью разработчика является решение проблемы параллелизма и безопасности потоков однопользовательского компонента.

я предлагаю читать статью ниже: Spring Singleton, Request, Session Beans and Thread Safety

1

As однотонный и несинхронизированный, Spring позволит любому количеству потоков одновременно вызывать isActiveAccount и refreshAccounts. Таким образом, этот класс не будет потокобезопасным и не уменьшит нагрузку на базу данных.

+0

ОК, последуйте за принятием: легко ли это можно устранить с помощью кода, включенного в этот класс Java (или исправления контекста приложения), или мне лучше пойти на решение для кеширования/базы данных? – thatidiotguy

+0

Что вы можете сделать, это использовать временный список для вызова в базу данных в 'refreshAccounts()'. Когда это вернется, синхронизируйтесь в 'account' и перепишите его в этот список. –

+0

Я бы сказал, определенно кеширование/база данных. Управлять параллелизмом самостоятельно сложно. С помощью кэширования вы, по крайней мере, можете запомнить для контроля параллелизма.Если вы действительно хотели любое количество запросов, я бы объявил прототип scope =. Затем вы столкнулись с проблемой загрузки, о которой вы беспокоились. –

2

Вы могли бы попросил разъяснений по моей initial answer. Spring не синхронизирует доступ к компоненту. Если у вас есть компонент в области по умолчанию (singleton), для этого компонента будет только один объект, и все параллельные запросы будут обращаться к этому объекту, требуя, чтобы этот объект был в потоке безопасным.

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

Самый простой способ сделать это - сделать поле счетов volatile. Это предполагает, что вы назначили новый список в поле после его заполнения (как вы, кажется, делаете).

private volatile List<Accounts> accounts; 
+0

Прошу прощения, я чувствовал, что было бы неискренним для названия другого вопроса, чтобы понять это в комментариях. Это действительно отдельная проблема. Как изменчивое решение сравнивается с отредактированным кодом, который я поставил выше? – thatidiotguy

+0

Это проще, без ожидания и, вероятно, немного более эффективно, чем явная блокировка (хотя эта разница незначительна при сравнении с I/O в базе данных). – meriton

0

У нас есть много таких метаданных и имеется 11 узлов. на каждом узле приложения у нас есть статические карты для таких данных, поэтому только один экземпляр, init от db при запуске один раз в нерабочее время каждый день или когда его поддерживает. имеют посредственный простой HTTP-почтовый API для отправки обновлений от одного узла к другим для некоторых данных, которые нам нужны обновления в реальном времени.

public AccountDao 
{ 
    private static List<Account> accounts; 
    private static List<String> activeAccounts; 
    private NamedParameterJdbcTemplate njt; 

    static { 
     try{ 
     refreshAccounts(); 
     }catch(Exception e){ 
     //log but do not throw. any uncaught exceptions in static means your class is un-usable 
     } 
    } 


    public AccountDao(Datasource ds) 
    { 
     this.njt = new NamedParameterJdbcTemplate(ds); 
     //refreshAccounts(); 
    } 

    /*called at creation, and then via API calls to inform service new users have 
    been added to the database by a separate program*/ 
    public void refreshAccounts() 
    { 
     this.accounts = /*call to database to get list of accounts*/ 
    } 

    public void addAccount(Account acEditedOrAdded) 
    { 
     //add or reove from map onr row 
     //can be called from this node or other node 
     //meaning if you have 2 nodes, keep IP port of each or use a internal web service or the like to tell 
     //node B when a account id added or changed in node A ... 
    } 

    //called by every request to web service 
    public static boolean isActiveAccount(String accountId) 
    { 
     Account a = map.get(accountId); 
     return a == null ? false : a.isActive(); 
    } 
} 
Смежные вопросы