การใช้งาน WeakReference ที่หายาก?
ฉันมีคลาสที่มีการเตรียมใช้งานอินสแตนซ์และใช้งานโดยการใช้แฟลตฟอร์ม
class MyAttributeConverter implements AttributeConverter<XX, YY> {
public YY convertToDatabaseColumn(XX attribute) { return null; }
public XX convertToEntityAttribute(YY dbData) { return null; }
}
ไม่มีอะไรผิดปกติและฉันคิดว่าฉันต้องเพิ่มวิธีการคงที่เพื่อใช้เป็นข้อมูลอ้างอิงวิธีการ
private static MyAttributeConverter instance;
// just a lazy-initialization;
// no synchronization is required;
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
if (instance == null) {
instance = new MyAttributeConverter();
}
return instance;
}
// do as MyAttributeConverter::toDatabaseColumn(xx)
public static YY toDatabaseColumn(XX attribute) {
return instance().convertToDatabaseColumn(attribute);
}
public static XX toEntityAttribute(YY dbData) {
return instance().convertToEntityAttribute(attribute);
}
ดูเหมือนจะไม่มีอะไรผิดปกติ (ฉันเชื่อ) และฉันไม่ชอบที่instanceอยู่กับชั้นเรียนและนั่นคือเหตุผลที่ฉันพยายามทำสิ่งนี้
private static WeakReference<MyAttributeConverter> reference;
public static <R> R applyInstance(Function<? super MyAttributeConverter, ? extends R> function) {
MyAttributeConverter referent;
if (reference == null) {
referent = new MyAttributeConverter();
refernce = new WeakReference<>(referent);
return applyInstance(function);
}
referent = reference.get();
if (referent == null) {
referent = new MyAttributeConverter();
refernce = new WeakReference<>(referent);
return applyInstance(function);
}
return function.apply(referent); // @@?
}
โดยพื้นฐานแล้วฉันไม่รู้ด้วยซ้ำว่าจะทดสอบโค้ดนี้อย่างไร และฉันขอโทษสำหรับคำถามของฉันซึ่งแต่ละคำถามอาจคลุมเครือ
- นี่เป็นแนวทาง (ถูก / ผิด) หรือไม่?
- มีโอกาสใด ๆ ที่
reference.get()ภายในfunction.applyสำนวนอาจจะnull? - มีโอกาสที่อาจเกิดปัญหาบางอย่างเช่นหน่วยความจำรั่วหรือไม่?
- ฉันควรพึ่งพา
SoftReferenceมากกว่าWeakReference?
ขอขอบคุณ.
คำตอบ
โปรดทราบว่าวิธีการเช่น
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
if (instance == null) {
instance = new MyAttributeConverter();
}
return instance;
}
ไม่ปลอดภัยเธรดเนื่องจากมีการอ่านสองinstanceฟิลด์ แต่ละคนอาจรับรู้การอัปเดตที่ทำโดยเธรดอื่นหรือไม่ นี่ก็หมายความว่าอ่านครั้งแรกในinstance == nullอาจจะรับรู้เป็นค่าใหม่เขียนโดยหัวข้ออื่นในขณะที่สองอาจประเมินค่าก่อนหน้านี้คือreturn instance; nullดังนั้นวิธีนี้อาจส่งคืนnullเมื่อมีเธรดมากกว่าหนึ่งเธรดกำลังดำเนินการพร้อมกัน นี่เป็นกรณีมุมที่หายาก แต่วิธีนี้ไม่ปลอดภัย คุณต้องมีตัวแปรภายในเพื่อให้แน่ใจว่าการทดสอบและคำสั่งส่งคืนใช้ค่าเดียวกัน
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
MyAttributeConverter current = instance;
if (current == null) {
instance = current = new MyAttributeConverter();
}
return current;
}
สิ่งนี้จะปลอดภัยMyAttributeConverterก็ต่อเมื่อไม่เปลี่ยนรูปโดยใช้finalช่องเท่านั้น มิฉะนั้นเธรดอาจส่งคืนอินสแตนซ์ที่สร้างโดยเธรดอื่นในสถานะที่สร้างขึ้นอย่างไม่สมบูรณ์
คุณสามารถใช้วิธีง่ายๆในการทำให้ปลอดภัยโดยไม่มีข้อ จำกัด เหล่านี้:
private static final MyAttributeConverter instance = new MyAttributeConverter();
private static MyAttributeConverter instance() {
return instance;
}
นี้ยังคงเป็นคนขี้เกียจเป็นเพียงการเริ่มต้นเรียนที่เกิดขึ้นในหนึ่งของทริกเกอร์ที่ระบุinstance()คือการภาวนาแรกของวิธีการ
การใช้งานของคุณWeakReferenceอาจมีปัญหาเดียวกัน นอกจากนี้ยังไม่ชัดเจนว่าเหตุใดคุณจึงหันไปใช้วิธีการเรียกใช้ซ้ำสองจุดที่คุณมีอาร์กิวเมนต์ที่ต้องการในตัวแปรท้องถิ่นอยู่แล้ว
การใช้งานที่ถูกต้องสามารถทำได้ง่ายกว่ามาก:
private static WeakReference<MyAttributeConverter> reference;
public static <R> R applyInstance(
Function<? super MyAttributeConverter, ? extends R> function) {
WeakReference<MyAttributeConverter> r = reference;
MyAttributeConverter referent = r != null? r.get(): null;
if (referent == null) {
referent = new MyAttributeConverter();
reference = new WeakReference<>(referent);
}
return function.apply(referent);
}
แต่ก่อนที่คุณจะใช้งานคุณควรพิจารณาใหม่ว่ารหัสที่ซับซ้อนนั้นคุ้มค่ากับความพยายามหรือไม่ ความจริงที่ว่าคุณยอมรับความจำเป็นในการสร้างวัตถุขึ้นใหม่เมื่อมีการรวบรวมขยะแม้กระทั่งการสร้างหลายอินสแตนซ์ในการเรียกใช้พร้อมกันขอแนะนำให้คุณทราบว่าการก่อสร้างจะมีราคาถูก เมื่อการก่อสร้างมีราคาถูกคุณอาจไม่จำเป็นต้องแคชอินสแตนซ์ของมันเลย
เพียงแค่พิจารณา
public static <R> R applyInstance(
Function<? super MyAttributeConverter, ? extends R> function) {
return function.apply(new MyAttributeConverter());
}
อย่างน้อยก็คุ้มค่าที่จะลองวัดประสิทธิภาพของแอปพลิเคชันและเปรียบเทียบกับแนวทางอื่น ๆ
ในทางกลับกันดูเหมือนว่าอินสแตนซ์ใช้หน่วยความจำจำนวนมากหรือไม่ถือทรัพยากรที่ไม่ใช่หน่วยความจำ มิฉะนั้นคุณกังวลมากขึ้นเกี่ยวกับความเป็นไปได้ที่จะมีอินสแตนซ์จำนวนมากบินไปมา ดังนั้นตัวแปรอื่น ๆ ที่ควรลองและเปรียบเทียบคือตัวแปรที่แสดงด้านบนโดยใช้static finalฟิลด์ที่มีการเริ่มต้นคลาสขี้เกียจและไม่มีโอกาสที่จะเก็บขยะจากวัตถุขนาดเล็กนั้น
การชี้แจงครั้งสุดท้าย คุณถาม
มีโอกาสใด ๆ ที่
reference.get()ภายในfunction.applyสำนวนอาจจะnull?
เนื่องจากไม่มีreference.get()การร้องขอในการประเมินfunction.applyจึงไม่มีโอกาสที่การร้องขอดังกล่าวจะประเมินถึงnullจุดนี้ ฟังก์ชันนี้ได้รับการอ้างอิงที่ชัดเจนและเนื่องจากรหัสการโทรทำให้มั่นใจได้ว่าการอ้างอิงที่แข็งแกร่งนี้ไม่ใช่การอ้างอิงnullจึงจะไม่กลายเป็นnullระหว่างการเรียกใช้applyวิธี
โดยทั่วไปตัวรวบรวมขยะจะไม่เปลี่ยนแปลงสถานะของแอปพลิเคชันในลักษณะที่โค้ดที่ใช้การอ้างอิงที่ชัดเจนจะสังเกตเห็นความแตกต่าง (ปล่อยให้หน่วยความจำว่างมากกว่า)
แต่เนื่องจากคุณถามเฉพาะเกี่ยวกับreference.get()การเก็บขยะอาจจะเก็บรวบรวมวัตถุหลังการใช้งานเป็นครั้งสุดท้าย , โดยไม่คำนึงถึงวิธีการหรือประหารชีวิตขอบเขตท้องถิ่น ดังนั้นผู้อ้างอิงสามารถรวบรวมได้ระหว่างการดำเนินการของapplyเมธอดเมื่อเมธอดนี้ไม่ใช้อ็อบเจกต์อีกต่อไป การเพิ่มประสิทธิภาพรันไทม์อาจทำให้สิ่งนี้เกิดขึ้นเร็วกว่าที่คุณคาดเดาได้โดยดูที่ซอร์สโค้ดเนื่องจากสิ่งที่อาจดูเหมือนการใช้อ็อบเจ็กต์ (เช่นการอ่านฟิลด์) อาจไม่ใช้อ็อบเจ็กต์ในรันไทม์ (เช่นเนื่องจากค่านั้นถูกเก็บไว้แล้วใน a การลงทะเบียน CPU ทำให้ไม่จำเป็นต้องเข้าถึงหน่วยความจำของวัตถุ) ตามที่กล่าวไว้ทั้งหมดนี้ไม่มีการปรับเปลี่ยนพฤติกรรมของวิธีการ
ดังนั้นสมมุติฐานreference.get()ในระหว่างการดำเนินการของapplyวิธีการโดยหลักการสามารถประเมินได้nullแต่ไม่มีเหตุผลที่น่ากังวลดังที่กล่าวไว้พฤติกรรมของapplyวิธีการไม่เปลี่ยนแปลง JVM จะเก็บหน่วยความจำของออบเจ็กต์ไว้ตราบเท่าที่จำเป็นเพื่อให้แน่ใจว่าวิธีการดำเนินการนี้ถูกต้อง
แต่คำอธิบายนั้นก็เพื่อความสมบูรณ์ ดังที่กล่าวไว้ว่าคุณไม่ควรใช้การอ้างอิงที่อ่อนแอหรืออ่อนสำหรับวัตถุที่ไม่ถือทรัพยากรราคาแพง