מבוא לאוספי Java מסונכרנים

1. סקירה כללית

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

אף על פי ששימוש באוספים לא מסונכרנים פשוטים הוא בסך הכל פשוט, אך הוא יכול גם להפוך לתהליך מרתיע ומועד לטעויות בעבודה בסביבות מרובות הליכי משנה (aka תכנות במקביל).

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

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

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

2. ה synchronizedCollection () שיטה

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

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

אוסף syncCollection = Collections.synchronizedCollection (ArrayList חדש ()); ListOperations ניתן להריצה = () -> {syncCollection.addAll (Arrays.asList (1, 2, 3, 4, 5, 6)); }; שרשור הברגה 1 = שרשור חדש (listOperations); הברגה 2 = שרשור חדש (listOperations); thread1.start (); thread2.start (); thread1.join (); thread2.join (); assertThat (syncCollection.size ()). isEqualTo (12); } 

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

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

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

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

3. ה synchronizedList () שיטה

כמו כן, דומה ל- synchronizedCollection () אנו יכולים להשתמש ב- synchronizedList () עטיפה ליצירת סנכרון רשימה.

כפי שניתן היה לצפות, השיטה מחזירה תצוגה בטוחה לשרשור של המצוין רשימה:

רשימה syncList = Collections.synchronizedList (ArrayList חדש ());

באופן לא מפתיע, השימוש ב- synchronizedList () השיטה נראית כמעט זהה למקבילה ברמה הגבוהה יותר, synchronizedCollection ().

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

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

רשימה syncCollection = Collections.synchronizedList (Arrays.asList ("a", "b", "c")); רשימת uppercasedCollection = ArrayList חדש (); ListOperations = () -> {מסונכרן (syncCollection) {syncCollection.forEach ((e) -> {uppercasedCollection.add (e.toUpperCase ());}); }}; 

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

השימוש ב- מסונכרן בלוק מבטיח את האטומיות של הפעולה.

4. ה synchronizedMap () שיטה

ה אוספים מחלקה מיישמת מעטפת סינכרון מסודרת אחרת, שנקראת synchronizedMap (). נוכל להשתמש בו ליצירת סנכרון בקלות מַפָּה.

השיטה מחזירה תצוגה בטוחה לשרשור של המסופק מַפָּה יישום:

מפה syncMap = Collections.synchronizedMap (HashMap חדש ()); 

5. ה synchronizedSortedMap () שיטה

יש גם יישום מקביל של ה- synchronizedMap () שיטה. זה נקרא synchronizedSortedMap (), שבה נוכל להשתמש ליצירת מסונכרן SortedMap למשל:

מפה syncSortedMap = Collections.synchronizedSortedMap (חדש TreeMap ()); 

6. ה synchronizedSet () שיטה

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

העטיפה מחזירה אוסף בטוח לחוטים המגובה במפורט מַעֲרֶכֶת:

הגדר syncSet = Collections.synchronizedSet (חדש HashSet ()); 

7. ה synchronizedSortedSet () שיטה

לבסוף, עטיפת הסנכרון האחרונה שנציג כאן היא synchronizedSortedSet ().

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

SortedSet syncSortedSet = Collections.synchronizedSortedSet (חדש TreeSet ()); 

8. אוספים מסונכרנים לעומת מקבילים

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

עכשיו, בואו נתמקד ההבדלים בין אוספים מסונכרנים לאוספים במקביל, כמו ConcurrentHashMap ו BlockingQueue יישומים.

8.1. אוספים מסונכרנים

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

כפי שניתן היה לצפות, אוספים מסונכרנים מבטיחים עקביות / שלמות נתונים בסביבות בעלות שרשור. עם זאת, הם עשויים להגיע לעונש בביצועים, מכיוון שרק חוט אחד יכול לגשת לאוסף בכל פעם (aka גישה מסונכרנת).

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

8.2. אוספים במקביל

אוספים במקביל (למשל. ConcurrentHashMap), להשיג בטיחות חוטים על ידי חלוקת הנתונים שלהם למקטעים. ב ConcurrentHashMap, למשל, שרשורים שונים יכולים לרכוש מנעולים בכל קטע, כך שרבדים שרשורים יכולים לגשת אל מַפָּה במקביל (aka גישה מקבילה).

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

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

9. מסקנה

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

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

כרגיל, כל דגימות הקוד המוצגות במאמר זה זמינות ב- GitHub.