Singleton: การใช้งานที่ปลอดภัยต่อเธรด
สร้าง Singleton ที่ปลอดภัยต่อเธรดด้วยการล็อกตรวจสอบซ้ำสองครั้ง enum หรือรูปแบบ holder
Singleton: การใช้งานที่ปลอดภัยต่อเธรด เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
รูปแบบซิงเกิลตัน
ซิงเกิลตันรับประกันว่าจะมีอินสแตนซ์ของคลาสอยู่เพียงหนึ่งเดียวใน JVM กรณีใช้งานที่พบบ่อย ได้แก่ ตัวจัดการการกำหนดค่า กลุ่มการเชื่อมต่อ และบริการ logging
ซิงเกิลตันแบบพื้นฐาน — ไม่ปลอดภัยต่อเธรด
ซิงเกิลตันแบบเริ่มต้นเมื่อจำเป็นแบบดั้งเดิมทำงานผิดพลาดเมื่อมีการเข้าถึงพร้อมกัน: สองเธรดอาจผ่านการตรวจสอบค่าว่างพร้อมกันและสร้างสองอินสแตนซ์ขึ้นมาได้
public class Config {
private static Config instance;
private Config() {}
public static Config getInstance() {
if (instance == null) { // race condition!
instance = new Config();
}
return instance;
}
}เมธอดแบบซิงโครไนซ์ — เรียบง่ายแต่ช้า
การเพิ่ม synchronized ให้กับ getInstance() ทำให้ปลอดภัยต่อเธรด แต่จะล็อกทุกครั้งที่เรียกใช้ แม้อินสแตนซ์จะมีอยู่แล้วก็ตาม จึงเป็นค่าใช้จ่ายที่ไม่จำเป็น
public static synchronized Config getInstance() {
if (instance == null) instance = new Config();
return instance;
}การล็อกแบบตรวจสอบสองครั้ง
ตรวจสอบโดยไม่ล็อกก่อน แล้วตรวจสอบอีกครั้งภายในบล็อก synchronized ต้องใช้ volatile กับฟิลด์เพื่อป้องกันการสลับลำดับคำสั่ง
public class Config {
private static volatile Config instance;
private Config() {}
public static Config getInstance() {
if (instance == null) {
synchronized (Config.class) {
if (instance == null) instance = new Config();
}
}
return instance;
}
}Holder สำหรับการเริ่มต้นเมื่อจำเป็น
ซิงเกิลตันแบบเริ่มต้นเมื่อจำเป็นที่ปลอดภัยและมีประสิทธิภาพที่สุดคือ ใช้คลาสซ้อนภายในแบบ static ที่เป็น private เก็บอินสแตนซ์ไว้ JVM จะโหลดคลาสนี้เมื่อมีการเรียก getInstance() ครั้งแรกเท่านั้น — การโหลดคลาสปลอดภัยต่อเธรดโดยธรรมชาติ
public class Config {
private Config() {}
private static final class Holder {
static final Config INSTANCE = new Config();
}
public static Config getInstance() {
return Holder.INSTANCE;
}
}ซิงเกิลตันแบบ enum
หนังสือ Effective Java แนะนำให้ใช้ enum ที่มีค่าคงที่เดียว วิธีนี้กระชับ ปลอดภัยต่อเธรด ปลอดภัยต่อการซีเรียลไลซ์ และไม่เสี่ยงต่อการโจมตีด้วยรีเฟลกชัน
public enum AppConfig {
INSTANCE;
private String apiUrl = "https://api.example.com";
public String getApiUrl() { return apiUrl; }
}
// Usage:
String url = AppConfig.INSTANCE.getApiUrl();การเริ่มต้นทันที
หากต้นทุนการเริ่มต้นระบบยอมรับได้ ให้เริ่มต้นอินสแตนซ์ขณะโหลดคลาสด้วยฟิลด์ static final วิธีนี้เรียบง่ายและปลอดภัยต่อเธรด แต่อินสแตนซ์จะถูกสร้างขึ้นเสมอแม้ไม่เคยถูกใช้งาน
public class Config {
private static final Config INSTANCE = new Config();
private Config() {}
public static Config getInstance() { return INSTANCE; }
}ซิงเกิลตันกับการซีเรียลไลซ์
ซิงเกิลตันที่ผ่านการซีเรียลไลซ์แล้วดีซีเรียลไลซ์จะสร้างอินสแตนซ์ใหม่ เว้นแต่คุณจะเพิ่ม readResolve() ที่คืนค่า INSTANCE แนวทางแบบ enum จัดการเรื่องนี้ให้โดยอัตโนมัติ
private Object readResolve() { return INSTANCE; }ซิงเกิลตันกับการโจมตีด้วยรีเฟลกชัน
รีเฟลกชันสามารถเรียกใช้คอนสตรักเตอร์ private ได้ ให้ป้องกันโดยโยนข้อยกเว้นจากคอนสตรักเตอร์หากมีอินสแตนซ์อยู่แล้ว ซิงเกิลตันแบบ enum ป้องกันปัญหานี้ได้ตามข้อกำหนดของ JVM
private Config() {
if (Holder.INSTANCE != null) {
throw new IllegalStateException("Use getInstance()");
}
}ซิงเกิลตันในเฟรมเวิร์กการฉีดการพึ่งพา
ใน Spring บีนจะเป็นซิงเกิลตันโดยค่าเริ่มต้น (@Scope("singleton")) โดยทั่วไปคุณแทบไม่ต้องใช้รูปแบบซิงเกิลตันด้วยตนเอง ให้คอนเทนเนอร์ DI จัดการแทน
@Component // Spring manages a single instance
public class FeatureFlags {
private final boolean darkMode = true;
public boolean isDarkMode() { return darkMode; }
}การทดสอบซิงเกิลตัน
ซิงเกิลตันยากต่อการทดสอบหน่วย เพราะมีสถานะส่วนกลาง ควรใช้การฉีดการพึ่งพาแทนโค้ดซิงเกิลตันที่เขียนเอง และฉีดอินสแตนซ์จำลองในการทดสอบผ่านการฉีดด้วยคอนสตรักเตอร์
ตรวจสอบความเข้าใจอย่างรวดเร็ว
แนวทางซิงเกิลตันใดที่ Effective Java แนะนำ
สรุปทบทวน
สำหรับซิงเกิลตันที่ปลอดภัยต่อเธรด ให้ใช้แนวทาง Holder สำหรับการเริ่มต้นเมื่อจำเป็นหรือแนวทางแบบ enum หลีกเลี่ยงการเริ่มต้นเมื่อจำเป็นแบบพื้นฐาน ในแอป Spring ให้เฟรมเวิร์กจัดการขอบเขตแทน
คำถามที่พบบ่อย
บทเรียน “Singleton: การใช้งานที่ปลอดภัยต่อเธรด” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “Singleton: การใช้งานที่ปลอดภัยต่อเธรด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “Singleton: การใช้งานที่ปลอดภัยต่อเธรด”
สร้าง Singleton ที่ปลอดภัยต่อเธรดด้วยการล็อกตรวจสอบซ้ำสองครั้ง enum หรือรูปแบบ holder คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “Singleton: การใช้งานที่ปลอดภัยต่อเธรด” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม
ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- Singleton: การใช้งานที่ปลอดภัยต่อเธรด
- รูปแบบ Factory Method
- Abstract Factory สำหรับตระกูลผลิตภัณฑ์
- รูปแบบ Builder กับ Fluent API