מדריך ל- ConcurrentSkipListMap

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

במאמר מהיר זה, נבחן את ה- ConcurrentSkipListMap כיתה מה java.util.concurrent חֲבִילָה.

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

נפתור בעיה של מיון זרם אירועים וקבלת תמונת מצב של האירועים שהגיעו ב- 60 השניות האחרונות באמצעות המבנה הזה.

2. לוגיקה של מיון זרמים

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

ראשית, נגדיר את מבנה נתוני האירוע שלנו:

אירוע בכיתה ציבורית {private ZonedDateTime eventTime; תוכן מחרוזת פרטי; // קונסטרוקטורים / גטרים סטנדרטיים}

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

ConcurrentSkipListMap אירועים = ConcurrentSkipListMap חדש (Comparator.comparingLong (v -> v.toInstant (). ToEpochMilli ()));

נשווה את כל האירועים שהגיעו באמצעות חותמות הזמן שלהם. אנו משתמשים ב- comparingLong () שיטה והעברת פונקציית החילוץ שיכולה לקחת א ארוך חותמת זמן מה ZonedDateTime.

כאשר האירועים שלנו מגיעים, עלינו רק להוסיף אותם למפה באמצעות ה- לָשִׂים() שיטה. שים לב ששיטה זו אינה דורשת סנכרון מפורש:

בטל ציבורי acceptEvent (אירוע אירוע) {events.put (event.getEventTime (), event.getContent ()); }

ה ConcurrentSkipListMap יטפל במיון האירועים שלמטה באמצעות משווה שהועבר אליו בבנאי.

היתרונות הבולטים ביותר של ConcurrentSkipListMap הן השיטות שיכולות ליצור תמונת מצב בלתי ניתנת לשינוי של הנתונים שלה בצורה נעילה. כדי להשיג את כל האירועים שהגיעו בדקה האחרונה, אנו יכולים להשתמש ב- tailMap () שיטה ולהעביר את הזמן שממנו אנו רוצים לקבל אלמנטים:

public ConcurrentNavigableMap public getEventsFromLastMinute () {return events.tailMap (ZonedDateTime.now (). minusMinutes (1)); } 

זה יחזיר את כל האירועים מהרגע האחרון. זו תהיה תמונת מצב בלתי ניתנת לשינוי ומה שהכי חשוב זה ששרשורי כתיבה אחרים יוכלו להוסיף אירועים חדשים ל ConcurrentSkipListMap ללא כל צורך לבצע נעילה מפורשת.

כעת אנו יכולים לקבל את כל האירועים שהגיעו מאוחר יותר בעוד דקה אחת - באמצעות ה- headMap () שיטה:

ציבורי ConcurrentNavigableMap getEventsOlderThatOneMinute () {return events.headMap (ZonedDateTime.now (). minusMinutes (1)); }

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

3. בדיקת לוגיקת זרם המיון

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

ExecutorService executorService = Executors.newFixedThreadPool (3); EventWindowSort eventWindowSort = EventWindowSort חדש (); int numberOfThreads = 2; מפיק ניתן להפעלה = () -> IntStream .rangeClosed (0, 100) .forEach (index -> eventWindowSort.acceptEvent (אירוע חדש (ZonedDateTime.now (). MinusSeconds (index), UUID.randomUUID (). ToString ())) ); עבור (int i = 0; i <numberOfThreads; i ++) {executorService.execute (מפיק); } 

כל חוט קורא ל acceptEvent () שיטה, שליחת האירועים שיש להם זמן אירוע מעכשיו ל"עכשיו מינוס מאה שניות ".

בינתיים נוכל להפעיל את getEventsFromLastMinute () שיטה שתחזיר את תמונת המצב של אירועים הנמצאים בתוך חלון הדקה:

ConcurrentNavigableMap eventsFromLastMinute = eventWindowSort.getEventsFromLastMinute ();

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

events longereOlderThanOneMinute = eventsFromLastMinute .entrySet () .stream () .filter (e -> e.getKey (). isBefore (ZonedDateTime.now (). minusMinutes (1))) .count (); assertEquals (eventsOlderThanOneMinute, 0);

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

long eventYoungerThanOneMinute = eventsFromLastMinute .entrySet () .stream () .filter (e -> e.getKey (). isAfter (ZonedDateTime.now (). minusMinutes (1))) .count (); assertTrue (eventYoungerThanOneMinute> 0);

שֶׁלָנוּ getEventsFromLastMinute () משתמש ב- tailMap () מתחת.

בואו נבדוק עכשיו את getEventsOlderThatOneMinute () שמשתמש ב- headMap () שיטה מה- ConcurrentSkipListMap:

ConcurrentNavigableMap eventsFromLastMinute = eventWindowSort.getEventsOlderThatOneMinute ();

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

events longereOlderThanOneMinute = eventsFromLastMinute .entrySet () .stream () .filter (e -> e.getKey (). isBefore (ZonedDateTime.now (). minusMinutes (1))) .count (); assertTrue (eventsOlderThanOneMinute> 0);

והבא, שאין אירוע אחד שנמצא מתוך הרגע האחרון:

long eventYoungerThanOneMinute = eventsFromLastMinute .entrySet () .stream () .filter (e -> e.getKey (). isAfter (ZonedDateTime.now (). minusMinutes (1))) .count (); assertEquals (eventYoungerThanOneMinute, 0);

הדבר החשוב ביותר שיש לציין הוא אנו יכולים לצלם את תמונת המצב בעוד שרשורים אחרים עדיין מוסיפים ערכים חדשים אל ה ConcurrentSkipListMap.

4. מסקנה

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

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

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


$config[zx-auto] not found$config[zx-overlay] not found