2011-01-07 30 views
7

Trong ứng dụng của tôi, có một lớp như dưới đây:Lớp khởi và đồng bộ phương pháp lớp

public class Client { 
    public synchronized static print() { 
     System.out.println("hello"); 
    } 

    static { 
     doSomething(); // which will take some time to complete 
    } 
} 

Lớp này sẽ được sử dụng trong một môi trường đa chủ đề, nhiều chủ đề có thể gọi phương thức Client.print() đồng thời . Tôi tự hỏi nếu có bất kỳ cơ hội mà thread-1 kích hoạt khởi tạo lớp, và trước khi khởi tạo lớp hoàn thành, thread-2 nhập vào phương thức in và in ra chuỗi "hello"?

Tôi thấy hành vi này trong hệ thống sản xuất (64 bit JVM + Windows 2008R2), tuy nhiên, tôi không thể sao chép hành vi này bằng chương trình đơn giản trong mọi môi trường.

Trong ngôn ngữ Java spec, phần 12.4.1 (http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html), nó nói:

Một loại hoặc loại giao diện T sẽ được khởi tạo ngay trước khi xảy ra lần đầu tiên của bất kỳ một trong những điều sau:

  • T là một lớp và một thể hiện của T được tạo.
  • T là một lớp và một phương thức tĩnh được khai báo bởi T được gọi.
  • Trường tĩnh được khai báo bằng T được gán.
  • Trường tĩnh được khai báo bằng T được sử dụng và tham chiếu đến trường không phải là hằng số biên dịch (§15.28). Các tham chiếu đến các hằng số biên dịch thời gian phải được giải quyết tại thời gian biên dịch thành một bản sao của giá trị hằng số thời gian biên dịch, vì vậy việc sử dụng một trường như vậy không bao giờ gây ra khởi tạo.

Theo văn bản này, việc khởi tạo lớp sẽ diễn ra trước khi gọi của phương pháp tĩnh, tuy nhiên, nó không phải là rõ ràng nếu khởi tạo lớp cần phải được hoàn trước khi gọi của phương pháp tĩnh . JVM nên ủy nhiệm hoàn thành việc khởi tạo lớp trước khi nhập phương thức tĩnh của nó theo trực giác của tôi và một số thử nghiệm của tôi hỗ trợ phỏng đoán của tôi. Tuy nhiên, tôi đã nhìn thấy hành vi ngược lại trong một môi trường khác. Ai đó có thể cho tôi một số ánh sáng về điều này?

Bất kỳ trợ giúp nào được đánh giá cao, cảm ơn.

Trả lời

4

sự hiểu biết của tôi về các văn bản trích dẫn là quá trình lớp khởi động xong (sẽ được khởi) trước một phương pháp tĩnh tuyên bố của T được gọi.

sẽ được khởi tạo ngụ ý rằng quá trình khởi tạo đã được bắt đầu và đã chấm dứt.

Vì vậy, nó không nên có thể (theo hiểu biết của tôi) rằng, trong khi bộ khởi tạo tĩnh được thực hiện vì Chủ đề A được gọi là print, một Chủ đề khác đã có thể gọi print.

Chapter 12.4.2 của JLS mô tả quy trình khởi tạo chi tiết, điều này đảm bảo việc khởi tạo các lớp trong môi trường đa luồng.

+0

Trình khởi tạo tĩnh là một phương thức lớp đơn giản được gọi dưới khóa (trình nạp lớp học). – bestsss

0

Nếu mã đang chạy trong một số vùng chứa như Servlet, bạn có thể khởi chạy nó trong vòng đời của vùng chứa.

3

Thi khối tĩnh coi là một phần của lớp khởi tạo:

Khởi tạo của một lớp bao gồm thực hiện initializers tĩnh của nó và initializers cho các lĩnh vực tĩnh (các biến lớp) được khai báo trong lớp ...

Nó được đảm bảo bởi đặc tả JVM sẽ được thực hiện theo cách an toàn. Để báo JLS section 12.4.2 Detailed Initialization Procedure:

Bởi vì ngôn ngữ lập trình Java là đa luồng, khởi tạo của một lớp hoặc giao diện đòi hỏi đồng bộ hóa cẩn thận, vì một số chủ đề khác có thể cố gắng để khởi tạo cùng lớp hoặc giao diện cùng một lúc. Ngoài ra còn có khả năng khởi tạo một lớp hoặc giao diện có thể được yêu cầu đệ quy như là một phần của việc khởi tạo của lớp hoặc giao diện đó; ví dụ, một biến khởi tạo trong lớp A có thể gọi một phương thức của một lớp B không liên quan, có thể lần lượt gọi một phương thức của lớp A. Việc thực thi máy ảo Java chịu trách nhiệm về việc đồng bộ hóa và khởi tạo đệ quy ...

cụ thể hơn, nó thực hiện bằng cách mua lại khóa trên đối tượng class:

thủ tục khởi tạo một lớp hoặc giao diện sau đó như sau:

  1. Đồng bộ hóa (§14.19) trên Lớp đối tượng đại diện cho lớp hoặc giao diện được khởi tạo

phương pháp của bạn là static synchronized và nó đòi hỏi khóa trên đối tượng Class là tốt. Vì cùng một khóa được JVM mua lại trong quá trình khởi tạo lớp, nên không thể cho một luồng khởi tạo lớp và cho phương thức thực thi khác là static synchronized trên đó. Tất cả các trích dẫn được lấy từ JLS Tôi hy vọng điều đó hữu ích. Btw, làm thế nào để bạn biết rằng in xảy ra trước khi khởi tạo lớp kết thúc?

EDIT: Thực sự tôi sai về giả định rằng chỉ static synchronized không thể được thực hiện song song với khởi tạo lớp học. Bất kỳ phương thức nào trên lớp không thể được thực hiện cho đến khi khởi tạo lớp kết thúc.

+0

@Petro, "Vì cùng một khóa được JVM mua lại trong quá trình khởi tạo lớp", điều này không chính xác vì trong phần JLS 12.4.2, nó nói "Nếu không, hãy ghi lại sự kiện khởi tạo đối tượng Class đang được tiến hành bởi thread hiện tại và giải phóng khóa trên đối tượng Class. Vì vậy, khóa lớp được phát hành trước khi khởi tạo lớp hoàn tất. – nybon

+0

Và bạn có thể xác minh hành vi này bằng 1) thread-1 kích hoạt khởi tạo lớp có thể mất nhiều thời gian để hoàn thành. 2) thread-2 sử dụng một câu lệnh được đồng bộ hóa để thu được khóa lớp như được đồng bộ hóa (MyClass.class). Nếu thời gian là ổn, thread-2 sẽ nhập vào khối lệnh được đồng bộ hóa. – nybon

+0

"làm thế nào để bạn biết rằng in xảy ra trước khi khởi tạo lớp kết thúc", tôi đã thêm một số tuyên bố in vào cuối lớp khởi tạo tĩnh để xác minh điều đó. – nybon

2

Nếu "môi trường nhiều chủ đề" sử dụng nhiều trình nạp lớp để tải lớp Ứng dụng khách, bạn có thể nhận các phiên bản máy khách, mỗi một trong số đó sẽ chạy trình khởi tạo tĩnh trước khi chạy bất kỳ Client.print() nào cuộc gọi. Bạn sẽ thấy một cái gì đó như

doSomething 
hello 
doSomething 
hello 
hello 
hello 

Tôi có một số mã mẫu cho thấy điều này nhưng phiên bản hiện tại hơi khó sử dụng. Nếu bạn muốn tôi có thể làm sạch nó và đăng nó.

+0

Ahh, tôi hoàn toàn quên về nhiều classloaders trong một JVM.Khá tốt! –

+0

Điểm tốt, tôi không bao giờ nghĩ về điều đó. có vẻ như không phải là trường hợp cho ứng dụng của tôi :( – nybon

Các vấn đề liên quan