เธรด Java Static initializers ปลอดภัยหรือไม่


138

ฉันใช้บล็อกรหัสคงที่เพื่อเริ่มต้นตัวควบคุมบางตัวในรีจิสทรีที่ฉันมี ดังนั้นคำถามของฉันคือฉันสามารถรับประกันได้หรือไม่ว่าบล็อกรหัสคงที่นี้จะถูกเรียกใช้เพียงครั้งเดียวเมื่อโหลดคลาสครั้งแรก ฉันเข้าใจว่าฉันไม่สามารถรับประกันได้ว่าจะเรียกบล็อกโค้ดนี้เมื่อใดฉันคาดเดาว่าเมื่อใดที่ Classloader โหลดครั้งแรก ฉันรู้ว่าฉันสามารถซิงโครไนซ์กับคลาสในบล็อกโค้ดแบบคงที่ได้ แต่ฉันเดาว่านี่คือสิ่งที่เกิดขึ้นจริงหรือ?

ตัวอย่างโค้ดง่ายๆจะเป็น;

class FooRegistry {

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

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

หรือฉันควรทำเช่นนี้

class FooRegistry {

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

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

10
ฉันไม่ชอบการออกแบบนี้เนื่องจากยังไม่สามารถทดสอบได้ ดูที่ Dependency Injection
dfa

คำตอบ:


201

ใช่ Java static initializers เป็นเธรดที่ปลอดภัย (ใช้ตัวเลือกแรกของคุณ)

อย่างไรก็ตามหากคุณต้องการให้แน่ใจว่าโค้ดถูกเรียกใช้งานทุกครั้งคุณต้องแน่ใจว่าคลาสนั้นโหลดโดยคลาสเดียวเท่านั้น การเริ่มต้นแบบคงที่จะดำเนินการหนึ่งครั้งต่อคลาสโหลดเดอร์


2
อย่างไรก็ตามคลาสสามารถโหลดได้โดยตัวโหลดคลาสหลายตัวดังนั้น addController อาจยังคงถูกเรียกมากกว่าหนึ่งครั้ง (ไม่ว่าคุณจะซิงโครไนซ์การโทรหรือไม่ก็ตาม) ...
Matthew Murdoch

4
อ๊ะเดี๋ยวก่อนเรากำลังบอกว่าจริงๆแล้วบล็อกโค้ดแบบคงที่ถูกเรียกใช้สำหรับตัวโหลดคลาสทุกตัวที่โหลดคลาส อืม ... ฉันเดาว่ามันน่าจะยังใช้ได้ แต่ฉันสงสัยว่าการเรียกใช้โค้ดประเภทนี้ใน OSGI env จะทำงานได้อย่างไรกับ
คลาสโหลดเดอร์แบบ

1
ใช่. บล็อกโค้ดแบบคงที่ถูกเรียกสำหรับคลาสโหลดเดอร์ทุกตัวที่โหลดคลาส
Matthew Murdoch

3
@ simon622 ใช่ แต่มันจะทำงานในคลาสออบเจ็กต์อื่นในแต่ละ ClassLoader คลาสอ็อบเจ็กต์ต่างๆที่ยังคงมีชื่อแบบเต็มเหมือนกัน แต่แสดงถึงประเภทที่แตกต่างกันซึ่งไม่สามารถส่งต่อกันได้
Erwin Bolwidt

1
หมายความว่าคีย์เวิร์ด 'สุดท้าย' ซ้ำซ้อนในตัวยึดอินสแตนซ์ใน: en.wikipedia.org/wiki/Initialization-on-demand_holder_idiomหรือไม่
spc16670

11

นี่เป็นเคล็ดลับที่คุณสามารถใช้สำหรับการเริ่มต้นที่ขี้เกียจ

enum Singleton {
    INSTANCE;
}

หรือก่อน Java 5.0

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

เนื่องจากบล็อกแบบคงที่ใน SingletonHolder จะทำงานหนึ่งครั้งในลักษณะที่ปลอดภัยของเธรดคุณจึงไม่จำเป็นต้องมีการล็อกอื่น ๆ คลาส SingletonHolder จะโหลดเมื่อคุณเรียกอินสแตนซ์ () เท่านั้น


18
คุณใช้คำตอบนี้จากข้อเท็จจริงที่ว่าบล็อกแบบคงที่จะถูกดำเนินการเพียงครั้งเดียวทั่วโลกซึ่งเป็นคำถามที่ถูกถาม
Michael Myers

2
ฉันคิดว่าสิ่งนี้ไม่ปลอดภัยเช่นกันในสภาพแวดล้อมตัวโหลดหลายชั้นสิ่งที่พูด.?
Ahmad

2
@ Ahmad Multi-class loader สภาพแวดล้อมได้รับการออกแบบมาเพื่อให้แต่ละแอปพลิเคชันมี singletons ของตัวเอง
Peter Lawrey

1
ไม่จำเป็นต้องมีคลาสที่ซ้อนกัน โครงสร้างนี้ใช้งานได้เหมือนกันเมื่อINSTANCEมีการประกาศเขตข้อมูลโดยตรงในSingleton(เช่นเดียวกับกรณีของenumตัวแปร)
Holger

4

ในสถานการณ์ปกติทุกอย่างในตัวเริ่มต้นแบบคงที่จะเกิดขึ้นก่อนทุกอย่างที่ใช้คลาสนั้นดังนั้นการซิงโครไนซ์จึงไม่จำเป็น อย่างไรก็ตามคลาสนี้สามารถเข้าถึงได้ทุกอย่างที่ผู้ควบคุมแบบคงที่เรียกใช้ (รวมถึงการเรียกใช้ตัวเริ่มต้นแบบคงที่อื่น ๆ )

คลาสสามารถโหลดได้โดยคลาสที่โหลด แต่ไม่จำเป็นต้องเริ่มต้นทันที แน่นอนว่าคลาสสามารถโหลดได้โดยหลายอินสแตนซ์ของตัวโหลดคลาสและด้วยเหตุนี้จึงกลายเป็นหลายคลาสที่มีชื่อเดียวกัน


3

ใช่เรียงลำดับ

staticinitializer เพียงได้รับการเรียกครั้งเดียวดังนั้นโดยความหมายปลอดภัยด้ายมันว่า - คุณจะต้องสองคนหรือมากกว่าสวดของstaticการเริ่มต้นที่จะได้รับการต่อสู้ด้าย

ที่กล่าวว่าstaticinitializers สร้างความสับสนในหลาย ๆ ด้าน ไม่มีลำดับที่ระบุจริงๆว่าพวกเขาถูกเรียกใช้ สิ่งนี้จะทำให้สับสนมากหากคุณมีสองคลาสที่มีตัวstaticเริ่มต้นขึ้นอยู่กับกันและกัน และถ้าคุณใช้คลาส แต่ไม่ได้ใช้สิ่งที่โปรแกรมstaticเริ่มต้นจะตั้งค่าไว้คุณจะไม่รับประกันว่าตัวโหลดคลาสจะเรียกใช้ตัวเริ่มต้นแบบคงที่

สุดท้ายโปรดจำไว้ว่าวัตถุที่คุณกำลังซิงโครไนซ์อยู่ ฉันตระหนักดีว่านี่ไม่ใช่สิ่งที่คุณกำลังถาม แต่ตรวจสอบให้แน่ใจว่าคำถามของคุณไม่ได้ถามจริงๆว่าคุณต้องทำให้addController()เธรดปลอดภัยหรือไม่


5
มีลำดับที่กำหนดไว้มากซึ่งเรียกว่า: ตามลำดับในซอร์สโค้ด
mafu

นอกจากนี้พวกเขาจะถูกเรียกเสมอไม่ว่าคุณจะใช้ผลของมันก็ตาม เว้นแต่จะมีการเปลี่ยนแปลงใน Java 6
mafu

8
ในชั้นเรียนตัวเริ่มต้นจะทำตามรหัส เนื่องจากคลาสสองคลาสขึ้นไปจึงไม่ได้กำหนดไว้ว่าคลาสใดจะได้รับการเตรียมใช้งานก่อนไม่ว่าคลาสหนึ่งจะได้รับการเริ่มต้น 100% ก่อนที่คลาสอื่นจะเริ่มหรือว่าสิ่งต่างๆ "แทรกสอด" กันอย่างไร เช่นถ้าสองคลาสแต่ละคลาสมีตัวเหนี่ยวนำแบบคงที่ที่อ้างถึงซึ่งกันและกันสิ่งต่างๆจะเร็วอย่างน่าเกลียด ฉันคิดว่ามีหลายวิธีที่คุณสามารถอ้างถึง int สุดท้ายแบบคงที่ไปยังคลาสอื่นที่ไม่มีการเรียกใช้ initializers แต่ฉันจะไม่เถียงประเด็นไม่ทางใดก็ทางหนึ่ง
Matt

มันน่าเกลียดและฉันจะหลีกเลี่ยง แต่มีวิธีที่กำหนดไว้สำหรับวิธีการแก้ไขวงจร อ้างถึง "The Java Programming Language 4th Edition": Page: 75, Section: 2.5.3. การเริ่มต้นแบบคงที่: "หากรอบเกิดขึ้นตัวเริ่มต้นแบบคงที่ของ X จะถูกดำเนินการจนถึงจุดที่เรียกใช้เมธอดของ Y เท่านั้นเมื่อ Y เรียกใช้เมธอด X เมธอดนั้นจะทำงานโดยที่ตัวเริ่มต้นแบบคงที่ที่เหลือซึ่งยังไม่ถูกเรียกใช้งาน "
JMI MADISON

0

ใช่ Static initializers ทำงานเพียงครั้งเดียว อ่านนี้สำหรับข้อมูลเพิ่มเติม


2
ไม่สามารถรันได้มากกว่าหนึ่งครั้ง
การชดใช้อย่าง จำกัด

5
ไม่สามารถเรียกใช้งานได้ครั้งเดียวต่อชั้นเรียน
ruurd

คำตอบพื้นฐาน: Static init ทำงานเพียงครั้งเดียว คำตอบขั้นสูง: Static init รันหนึ่งครั้งต่อคลาส loader ความคิดเห็นแรกสับสนเพราะการใช้วลีผสมคำตอบทั้งสองนี้
JMI MADISON

-4

โดยพื้นฐานแล้วเนื่องจากคุณต้องการอินสแตนซ์ซิงเกิลตันคุณควรทำแบบเก่าไม่มากก็น้อยและตรวจสอบให้แน่ใจว่าอ็อบเจ็กต์ซิงเกิลตันของคุณเริ่มต้นเพียงครั้งเดียวและครั้งเดียวเท่านั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.