2009-11-30 59 views
10

Tôi có một số mã java mà được và đặt một thuộc tính phiên:Sử dụng request.getSession() làm đối tượng khóa?

Object obj = session.getAttribute(TEST_ATTR); 
if (obj==null) { 
    obj = new MyObject(); 
    session.setAttribute(obj); 
} 

Để làm cho thread-safe mã này, tôi muốn quấn nó trong một khối đồng bộ. Nhưng tôi sử dụng cái gì làm đối tượng khóa? Nó có ý nghĩa để sử dụng phiên không?

synchronized (session) { 
    Object obj = session.getAttribute(TEST_ATTR); 
    if (obj==null) { 
    obj = new MyObject(); 
    session.setAttribute(obj); 
    } 
} 

Trả lời

3

Thường không được sử dụng khóa mà bạn không có quyền kiểm soát. Một khóa nên được scoped càng chặt chẽ càng tốt và kể từ khi phiên là nhiều hơn hoặc ít hơn một đối tượng toàn cầu, nó không phù hợp với hóa đơn. Hãy thử sử dụng một khóa riêng biệt từ gói java.util.concurrent.locks và phạm vi đó cho lớp học của bạn.

+0

Đây không phải là khóa kiểm tra kép, đây chỉ là đặt giá trị trong bản đồ nếu nó chưa tồn tại. – Yishai

+0

Đồng ý, sớm. Đã xóa. – Kevin

+0

Làm cho khóa tĩnh thành lớp của tôi (không phải là một servlet, nhưng một lớp được tạo ra từ bên trong một trang jsp) đảm bảo rằng tất cả các yêu cầu đang đồng bộ hóa trên cùng một khóa? – MCS

1

Mã của bạn sẽ không hoạt động vì ít nhất hai lý do.

1) Nếu phiên không tồn tại thì bạn có thể dễ dàng tạo phiên đó hai lần cho cùng một người dùng và có trạng thái chạy xấu.

2) Nếu phiên không phải là cùng một đối tượng trong chuỗi, thì nó sẽ không hoạt động. Phiên có thể sẽ là equals() cho cùng một phiên trong một chuỗi khác, nhưng phiên đó sẽ không hoạt động.

3

Trong ngữ cảnh của servlets? Servlets có thể được phân phối trên nhiều quy trình, do đó bạn không thể luôn có cùng một đối tượng phiên. Một hệ quả của việc này là một thùng chứa servlet có thể quyết định cung cấp cho bạn một đối tượng phiên khác trong cùng một tiến trình.

IIRC, Brian Goetz đã viết một bài viết thú vị về khó khăn trong việc làm đúng với phiên.

Lời khuyên của tôi: Hãy tránh các phiên càng nhiều càng tốt và không khóa các đối tượng ngẫu nhiên (sử dụng vật thể khóa không có mục đích khác).

+2

Đây có phải là bài viết: http://www.ibm.com/developerworks/library/j-jtp09238.html không? Ông thảo luận sử dụng một khối đồng bộ nhưng không phải là những gì để sử dụng như khóa. – MCS

+0

Tôi nghĩ anh ta đi đến kết luận rằng không có khóa an toàn. –

3

Tôi đã xem bài viết bạn đã đăng. Bạn có thể bỏ qua việc đồng bộ hóa tất cả lại với nhau và đưa phương pháp tương tự mà tác giả đã làm bằng cách sử dụng so sánh-và-set để đảm bảo rằng dữ liệu của bạn là chính xác:

ServletContext ctx = getServletConfig().getServletContext(); 
AtomicReference<TYPE> holder 
    = (AtomicReference<TYPE>) ctx.getAttribute(TEST_ATTR); 
while (true) { 
    TYPE oldVal = holder.get(); 
    TYPE newVal = computeNewVal(oldVal); 
    if (holder.compareAndSet(oldVal, newVal)) 
     break; 
} 

holder.compareAndSet (cũ, mới) sẽ trả về false nếu một số chủ đề khác cập nhật giá trị của "chủ sở hữu" kể từ lần cuối bạn đọc nó. holder.compareAndSet (,) được đặt trong một vòng lặp while (true) để nếu giá trị thay đổi trước khi bạn có thể viết nó thì bạn sẽ có cơ hội đọc lại giá trị và thử viết lại.

http://java.sun.com/javase/6/docs/api/java/util/concurrent/atomic/AtomicReference.html

2

Các spec không đảm bảo rằng điều này sẽ giúp cả:

synchronized (session) { 
    Object obj = session.getAttribute(TEST_ATTR); 
    if (obj==null) { 
    obj = new MyObject(); 
    session.setAttribute(obj); 
    } 
} 

(Nó có thể làm việc cho triển khai cụ thể, nhưng có gì đảm bảo rằng nó sẽ làm việc trong tất cả các container.)

Servlet 2.5 MR6 nói:

Nhiều servlet thực hiện các chuỗi yêu cầu có thể có quyền truy cập hoạt động vào cùng một đối tượng phiên cùng một lúc. Vùng chứa phải đảm bảo rằng thao tác các cấu trúc dữ liệu nội bộ đại diện cho các thuộc tính phiên được thực hiện theo cách thức luồng an toàn. Nhà phát triển có trách nhiệm truy cập chủ đề an toàn đối với các đối tượng thuộc tính.Điều này sẽ bảo vệ bộ sưu tập thuộc tính bên trong đối tượng HttpSession khỏi truy cập đồng thời, loại bỏ cơ hội cho một ứng dụng khiến cho bộ sưu tập đó bị hỏng.

Về cơ bản, thông số kỹ thuật làm cho nó trở thành vấn đề của bạn. Giải pháp của bạn sẽ phải được điều chỉnh theo kế hoạch thiết kế và triển khai ứng dụng của bạn. Tôi không chắc rằng có một giải pháp toàn cầu cho vấn đề sẽ làm việc trong mọi trường hợp; tuy nhiên, bạn có thể nhận được câu trả lời tốt hơn nếu bạn cụ thể hơn về kiến ​​trúc của ứng dụng và cấu hình của máy chủ ứng dụng của bạn.

1

Bạn không cần khóa vì session.setAttribute() là chủ đề an toàn (xem chú thích đặc tả servlet từ @McDowell ở trên).

Tuy nhiên, hãy sử dụng một ví dụ khác. Giả sử bạn muốn chek giá trị của thuộc tính, sau đó cập nhật nó nếu < = 100. Trong trường hợp này, bạn cần phải đồng bộ hóa khối mã cho số getAttribute() so sánh < = 100 và setAttribute().

Bây giờ, bạn nên sử dụng khóa nào? Hãy nhớ rằng không có syncronization nếu các đối tượng khác nhau được sử dụng cho khóa. Vì vậy, các khối mã khác nhau phải sử dụng cùng một đối tượng. Lựa chọn đối tượng session của bạn có thể tốt. Hãy nhớ rằng các khối mã khác nhau có thể truy cập phiên (cả đọc/ghi) ngay cả khi bạn đã lấy khóa, trừ khi mã khác cũng khóa trên đối tượng phiên. Một lỗ hổng ở đây là có quá nhiều chỗ trong mã của bạn lấy một khóa trên đối tượng phiên và do đó phải đợi. Ví dụ, nếu khối mã của bạn sử dụng thuộc tính phiên A và một đoạn mã khác sử dụng thuộc tính phiên B, nó sẽ là tốt đẹp nếu chúng không cần phải chờ đợi lẫn nhau bằng cách lấy khóa trên đối tượng phiên. Việc sử dụng các đối tượng tĩnh có tên LockForA và LockForB có thể là lựa chọn tốt hơn cho mã của bạn để sử dụng - ví dụ: synchronized (LockForA) { }.

0

Tôi đã có cùng một vấn đề và không muốn phạm vi nó vào lớp của tôi bởi vì việc tạo đối tượng của tôi có thể mất một chủ đề thứ hai và gian hàng của các phiên khác nơi đối tượng đã được tạo. Tôi không muốn sử dụng đối tượng phiên của yêu cầu để đồng bộ hóa vì việc triển khai máy chủ ứng dụng có thể trả về một mặt khác của phiên trong các yêu cầu khác nhau và đồng bộ hóa trên các đối tượng khác nhau. Vì vậy, tôi chọn để đặt một đối tượng trên phiên làm việc để sử dụng như một khóa và sử dụng thành ngữ kiểm tra kép để đảm bảo khóa chỉ được tạo một lần. Đồng bộ hóa trong phạm vi MyObject không còn là vấn đề nữa vì việc tạo một đối tượng khá nhanh.

Object lock = session.getAttribute("SessionLock"); 
if (lock == null) { 
    synchronized (MyObject.class) { 
    lock = session.getAttribute("SessionLock"); 
    if(lock == null) { 
     lock = new Object(); 
     session.setAttribute("SessionLock", lock); 
    } 
    } 
} 
synchronized (lock) { 
    Object obj = session.getAttribute(TEST_ATTR); 
    if (obj==null) { 
    obj = new MyObject(); 
    session.setAttribute(obj); 
    } 
} 
Các vấn đề liên quan