2013-03-13 32 views
8

Nếu tôi có một lớp Java định nghĩa dưới đây được tiêm trong ứng dụng web của tôi qua dependency injection:Xuân Singleton Chủ đề An toàn

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

Tôi lo ngại về an toàn thread. Khung công tác Spring không xử lý các trường hợp mà một yêu cầu đang đọc từ danh sách và nó hiện đang được cập nhật bởi một danh sách khác? Tôi đã sử dụng khóa đọc/ghi trước đây trong các ứng dụng khác, nhưng tôi chưa bao giờ nghĩ về một trường hợp như trên trước đây.

Tôi đã lên kế hoạch sử dụng bean làm đĩa đơn để tôi có thể giảm tải cơ sở dữ liệu.

Bằng cách này, đây là một tiếp lên các câu hỏi dưới đây:

Java Memory Storage to Reduce Database Load - Safe?

EDIT:

Vì vậy, sẽ mã như thế này giải quyết vấn đề này:

/*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(); 
     } 

Trả lời

13

Khung công tác Spring không thực hiện bất kỳ điều gì dưới mui xe liên quan đến hành vi đa luồng của một bean đơn. Đó là trách nhiệm của nhà phát triển để đối phó với vấn đề đồng thời và an toàn luồng của đậu đơn.

tôi sẽ khuyên bạn nên đọc bài viết dưới đây: Spring Singleton, Request, Session Beans and Thread Safety

1

Như một singleton và không đồng bộ, Spring sẽ cho phép bất kỳ số lượng chủ đề nào đồng thời gọi isActiveAccountrefreshAccounts. Vì vậy, không có lớp này sẽ không được thread-an toàn và sẽ không làm giảm tải cơ sở dữ liệu.

+0

Ok, theo dõi để chấp nhận: Điều này có thể sửa chữa dễ dàng thông qua mã được bao gồm trong lớp Java này (hoặc sửa lỗi bối cảnh ứng dụng) hay không? – thatidiotguy

+0

Những gì bạn có thể làm là sử dụng một danh sách tạm thời để thực hiện cuộc gọi đến cơ sở dữ liệu trong 'refreshAccounts()'. Khi trả về, đồng bộ hóa trên 'tài khoản' và gán lại nó cho danh sách đó. –

+0

Tôi sẽ nói chắc chắn bộ nhớ đệm/cơ sở dữ liệu. Việc tự quản lý đồng thời là khó khăn. Với bộ nhớ đệm bạn có thể ít nhất có ghi nhớ để kiểm soát đồng thời.Nếu bạn thực sự muốn bất kỳ số lượng yêu cầu nào tôi sẽ khai báo scope = prototype. Sau đó, bạn chạy vào vấn đề tải bạn quan tâm. –

2

Bạn có thể đề nghị làm rõ initial answer tôi. Spring không đồng bộ hóa quyền truy cập vào bean. Nếu bạn có một bean trong phạm vi mặc định (singleton), sẽ chỉ có một đối tượng duy nhất cho bean đó, và tất cả các yêu cầu đồng thời sẽ truy cập đối tượng đó, yêu cầu đối tượng đó vào luồng an toàn.

Hầu hết các hạt đậu mùa xuân không có trạng thái có thể thay đổi được, và như vậy là chuỗi an toàn không đáng kể. Bean của bạn có trạng thái có thể thay đổi được, vì vậy bạn cần đảm bảo không có luồng nào nhìn thấy một danh sách các tài khoản mà luồng khác hiện đang được lắp ráp.

Cách dễ nhất để làm điều đó là tạo trường tài khoản volatile. Điều đó giả định rằng bạn gán danh sách mới cho trường sau khi đã điền nó (khi bạn đang làm việc).

private volatile List<Accounts> accounts; 
+0

Tôi xin lỗi, tôi cảm thấy rằng nó sẽ là không trung thực với tiêu đề của câu hỏi khác để có được vào đó trong ý kiến. Nó thực sự là một vấn đề riêng biệt. Làm thế nào để giải pháp dễ bay hơi so sánh với mã đã chỉnh sửa mà tôi đặt ở trên? – thatidiotguy

+0

Nó đơn giản hơn, chờ đợi và có thể hiệu quả hơn một chút so với khóa rõ ràng (mặc dù sự khác biệt đó là không đáng kể khi so sánh nó với I/O với cơ sở dữ liệu). – meriton

0

chúng tôi có nhiều dữ liệu meta và có 11 nút đang chạy. trên mỗi nút ứng dụng, chúng tôi có bản đồ tĩnh cho dữ liệu như vậy, chỉ một thể hiện của nó, bắt đầu từ db khi khởi động một lần vào giờ cao điểm ngoài giờ mỗi ngày hoặc khi người hỗ trợ kích hoạt nó. có một API http dựa trên bài đăng đơn giản giữa các trang để gửi các bản cập nhật từ nút này đến nút khác cho một số dữ liệu mà chúng tôi cần cập nhật trong thời gian thực.

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(); 
    } 
} 
Các vấn đề liên quan