Các luồng khởi tạo tĩnh Java có an toàn không?


136

Tôi đang sử dụng một khối mã tĩnh để khởi tạo một số bộ điều khiển trong sổ đăng ký mà tôi có. Do đó, câu hỏi của tôi là, tôi có thể đảm bảo rằng khối mã tĩnh này sẽ chỉ được gọi hoàn toàn một lần khi lớp được tải lần đầu tiên không? Tôi hiểu rằng tôi không thể đảm bảo khi nào khối mã này sẽ được gọi, tôi đoán nó khi Trình tải lớp đầu tiên tải nó. Tôi nhận ra rằng tôi có thể đồng bộ hóa trên lớp trong khối mã tĩnh, nhưng tôi đoán đây thực sự là những gì xảy ra?

Ví dụ mã đơn giản sẽ là;

class FooRegistry {

    static {
        //this code must only ever be called once 
        addController(new FooControllerImpl());
    }

    private static void addController(IFooController controller) { 
        // ...
    }
}

hoặc tôi nên làm điều này;

class FooRegistry {

    static {
        synchronized(FooRegistry.class) {
            addController(new FooControllerImpl());
        }
    }

    private static void addController(IFooController controller) {
        // ...
    }
}

10
Tôi không thích thiết kế này, vì nó là không thể. Có một cái nhìn về tiêm phụ thuộc.
dfa

Câu trả lời:


199

Có, các trình khởi tạo tĩnh Java là luồng an toàn (sử dụng tùy chọn đầu tiên của bạn).

Tuy nhiên, nếu bạn muốn đảm bảo rằng mã được thực thi chính xác một khi bạn cần đảm bảo rằng lớp chỉ được tải bởi một trình nạp lớp duy nhất. Khởi tạo tĩnh được thực hiện một lần trên mỗi trình nạp lớp.


2
Tuy nhiên, một lớp có thể được tải bởi nhiều trình tải lớp, vì vậy addContoder vẫn có thể được gọi nhiều lần (bất kể bạn có đồng bộ hóa cuộc gọi hay không) ...
Matthew Murdoch

4
À, vậy thì chúng ta đang nói rằng khối mã tĩnh thực sự được gọi cho mọi trình nạp lớp tải lớp.? Hmm ... Tôi đoán điều này vẫn ổn, tuy nhiên, tôi tự hỏi làm thế nào để chạy loại mã này trong một OSGI env sẽ hoạt động, với các trình nạp lớp gói mulitple ..
simon622

1
Đúng. Khối mã tĩnh được gọi cho mọi trình nạp lớp tải lớp.
Matthew Murdoch

3
@ simon622 Có, nhưng nó sẽ hoạt động trong một đối tượng lớp khác nhau trong mỗi ClassLoader. Các đối tượng Lớp khác nhau vẫn có cùng tên đủ điều kiện, nhưng đại diện cho các loại khác nhau không thể truyền cho nhau.
Erwin Bolwidt

1
điều này có nghĩa là từ khóa 'cuối cùng' là dự phòng trong chủ sở hữu cá thể trong: en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom ?
spc16670

11

Đây là một mẹo bạn có thể sử dụng để khởi tạo lười biếng

enum Singleton {
    INSTANCE;
}

hoặc cho Java 5.0 trước

class Singleton {
   static class SingletonHolder {
      static final Singleton INSTANCE = new Singleton();
   }
   public static Singleton instance() {
      return SingletonHolder.INSTANCE;
   }
}

Vì khối tĩnh trong SingletonHolder sẽ chạy một lần theo cách an toàn của luồng, bạn không cần bất kỳ khóa nào khác. Lớp SingletonHolder sẽ chỉ được tải khi bạn gọi cá thể ()


18
Bạn đang dựa trên câu trả lời này trên thực tế rằng khối tĩnh sẽ chỉ được thực hiện một lần trên toàn cầu - đó là câu hỏi đã được hỏi.
Michael Myers

2
Tôi nghĩ điều này cũng không an toàn trong môi trường trình tải nhiều lớp nói gì.?
Ahmad

2
@Ahmad Môi trường trình tải đa lớp được thiết kế để cho phép mỗi ứng dụng có các singletons riêng.
Peter Lawrey

4

Trong các trường hợp thông thường, mọi thứ trong trình khởi tạo tĩnh xảy ra - trước khi mọi thứ sử dụng lớp đó, do đó thường không cần đồng bộ hóa. Tuy nhiên, lớp có thể truy cập được với bất cứ thứ gì mà trình điều khiển tĩnh gọi (bao gồm cả việc khởi tạo tĩnh khác được gọi).

Một lớp có thể được tải bởi một lớp được tải nhưng không nhất thiết phải được khởi tạo ngay lập tức. Tất nhiên, một lớp có thể được tải bởi nhiều phiên bản của trình nạp lớp và do đó trở thành nhiều lớp có cùng tên.


3

Ừ kiểu vậy, chắc vậy

Trình statickhởi tạo chỉ được gọi một lần, do đó, theo định nghĩa đó, chuỗi này an toàn - bạn cần hai hoặc nhiều lệnh gọi củastatic khởi tạo để thậm chí có được sự tranh chấp luồng.

Điều đó nói rằng, statickhởi tạo là khó hiểu theo nhiều cách khác nhau. Thực sự không có thứ tự cụ thể mà chúng được gọi. Điều này thực sự gây nhầm lẫn nếu bạn có hai lớp có trình statickhởi tạo phụ thuộc lẫn nhau. Và nếu bạn sử dụng một lớp nhưng không sử dụng những gì trình statickhởi tạo sẽ thiết lập, bạn không được đảm bảo trình tải lớp sẽ gọi trình khởi tạo tĩnh.

Cuối cùng, hãy ghi nhớ các đối tượng bạn đang đồng bộ hóa. Tôi nhận ra đây không thực sự là những gì bạn đang hỏi, nhưng hãy chắc chắn rằng câu hỏi của bạn không thực sự hỏi nếu bạn cần làm cho addController()luồng an toàn.


5
Có một thứ tự rất xác định trong đó chúng được gọi là: Theo thứ tự trong mã nguồn.
mafu

Ngoài ra, họ luôn được gọi, bất kể bạn sử dụng kết quả của họ. Trừ khi điều này được thay đổi trong Java 6.
mafu

8
Trong một lớp, người khởi tạo tuân theo mã. Cho hai hoặc nhiều lớp, nó không được định nghĩa là lớp nào được khởi tạo trước, cho dù một lớp được khởi tạo 100% trước khi bắt đầu lớp khác hay cách mọi thứ được "xen kẽ". Vd Tôi nghĩ rằng có nhiều cách bạn có thể đề cập đến một int cuối cùng tĩnh cho một lớp khác với việc gọi các trình khởi tạo nhưng tôi sẽ không tranh luận điểm này bằng cách này hay cách khác
Matt

Nó trở nên xấu xí, và tôi sẽ tránh nó. Nhưng có một cách xác định cho cách giải quyết các chu kỳ. Trích dẫn "Ngôn ngữ lập trình Java Phiên bản thứ 4": Trang: 75, Mục: 2.5.3. Khởi tạo tĩnh: "Nếu chu kỳ xảy ra, bộ khởi tạo tĩnh của X sẽ chỉ được thực thi đến điểm mà phương thức của Y được gọi. Khi đến lượt Y, gọi phương thức X, phương thức đó chạy với phần còn lại của bộ khởi tạo tĩnh chưa được thực thi "
JMI MADISON

0

Có, bộ khởi tạo tĩnh chỉ được chạy một lần. Đọc này để biết thêm thông tin .


2
Không, chúng có thể được chạy nhiều hơn một lần.
Chuộc tội có giới hạn

5
Không, chúng có thể được chạy một lần M PERI LỚP HỌC.
ruurd

Câu trả lời cơ bản: Khởi động tĩnh chỉ chạy một lần. Câu trả lời nâng cao: Khởi động tĩnh chạy một lần cho mỗi trình nạp lớp. Nhận xét đầu tiên là khó hiểu vì phrasing trộn hai câu trả lời này.
JMI MADISON

-4

Vì vậy, về cơ bản, vì bạn muốn có một cá thể đơn lẻ, bạn nên thực hiện nó ít nhiều theo cách lỗi thời và đảm bảo đối tượng singleton của bạn được khởi tạo một lần và chỉ một lần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.