נעילה כפולה עם סינגלטון

1. הקדמה

במדריך זה נדבר על דפוס עיצוב הנעילה שנבדק כפול. דפוס זה מצמצם את מספר רכישות המנעולים על ידי בדיקת מצב הנעילה פשוט לפני כן. כתוצאה מכך, בדרך כלל יש שיפור ביצועים.

בואו נסתכל לעומק על איך זה עובד.

2. יישום

ראשית, נבחן יחיד יחיד עם סנכרון דרקוני:

מעמד ציבורי DraconianSingleton {פרטי סטטי DraconianSingleton סטטי; DraconianSingleton מסונכרן סטטי ציבורי getInstance () {if (מופע == null) {מופע = DraconianSingleton חדש (); } חזרה מופע; } // קונסטרוקטור פרטי ושיטות אחרות ...}

למרות שהשיעור הזה מוגן בחוטים, אנו יכולים לראות שיש חסרון ברור בביצועים: בכל פעם שאנחנו רוצים לקבל את המופע של הסינגלטון שלנו, עלינו לרכוש נעילה שעלולה להיות מיותרת.

כדי לתקן את זה, במקום זאת נוכל לאמת אם עלינו ליצור את האובייקט מלכתחילה ורק במקרה זה נרכוש את המנעול.

בהמשך הדרך, אנו רוצים לבצע את אותה בדיקה שוב ברגע שאנחנו נכנסים לבלוק המסונכרן, כדי לשמור על הפעולה אטומית:

מחלקה ציבורית DclSingleton {מופע DclSingleton סטטי פרטי; DclSingleton סטטי ציבורי getInstance () {if (מופע == null) {מסונכרן (DclSingleton .class) {if (מופע == null) {מופע = DclSingleton חדש (); }}} להחזיר מופע; } // קונסטרוקטור פרטי ושיטות אחרות ...}

דבר אחד שיש לזכור עם דפוס זה הוא השדה צריך להיות נָדִיף כדי למנוע בעיות בקוהרנטיות במטמון. למעשה, מודל הזיכרון של Java מאפשר פרסום של אובייקטים מאותחלים חלקית וזה עשוי להוביל בתורו לבאגים עדינים.

3. חלופות

למרות שהנעילה המסומנת כפול יכולה להאיץ את העניינים, יש לה לפחות שתי בעיות:

  • מכיוון שזה דורש את נָדִיף מילת המפתח תפעל כראוי, היא אינה תואמת לג'אווה 1.4 ולגרסאות נמוכות יותר
  • זה די מילולי וזה מקשה על הקריאה של הקוד

מסיבות אלה, בואו נבדוק כמה אפשרויות אחרות ללא פגמים אלה. כל השיטות הבאות מאצילות את משימת הסנכרון ל- JVM.

3.1. אתחול מוקדם

הדרך הקלה ביותר להשיג בטיחות חוטים היא להטמיע את יצירת האובייקט או להשתמש בבלוק סטטי שווה ערך. זה מנצל את העובדה ששדות ובלוקים סטטיים מאותחלים בזה אחר זה (Java Language Specification 12.4.2):

מחלקה ציבורית EarlyInitSingleton {גמר סטטי פרטי פרטי EarlyInitSingleton INSTANCE = חדש EarlyInitSingleton (); סטטי ציבורי EarlyInitSingleton getInstance () {return INSTANCE; } // קונסטרוקטור פרטי ושיטות אחרות ...}

3.2. אתחול לפי דרישה

בנוסף, מכיוון שאנו יודעים מההפניה למפרט שפת Java בפסקה הקודמת כי אתחול מחלקה מתרחש בפעם הראשונה שאנו משתמשים באחת מהשיטות או השדות שלו, אנו יכולים להשתמש בכיתה סטטית מקוננת ליישום אתחול עצל:

מחלקה ציבורית InitOnDemandSingleton {מחלקה סטטית פרטית InstanceHolder {סופית סטטית פרטית InitOnDemandSingleton INSTANCE = חדש InitOnDemandSingleton (); } InitOnDemandSingleton סטטי ציבורי getInstance () {return InstanceHolder.INSTANCE; } // קונסטרוקטור פרטי ושיטות אחרות ...}

במקרה זה, InstanceHolder הכיתה תקצה את השדה בפעם הראשונה שאנו ניגשים אליו באמצעות קריאה getInstance.

3.3. אנום סינגלטון

הפתרון האחרון מגיע מה- ג'אווה יעילה ספר (פריט 3) מאת יהושע בלוק ומשתמש בספר enum במקום מעמד. בזמן הכתיבה זו נחשבת לדרך התמציתית והבטוחה ביותר לכתוב יחיד:

enum ציבור EnumSingleton {INSTANCE; // שיטות אחרות ...}

4. מסקנה

לסיכום, מאמר מהיר זה עבר את דפוס הנעילה שנבדק כפול, גבולותיו וכמה חלופות.

בפועל, הפירוט המוגזם וחוסר התאימות לאחור הופכים את הדפוס למועד לטעות ולכן עלינו להימנע ממנו. במקום זאת, עלינו להשתמש בחלופה המאפשרת ל- JVM לבצע את הסנכרון.

כמו תמיד, הקוד של כל הדוגמאות זמין ב- GitHub.