מדריך ל- Java Phaser

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

במאמר זה נבחן את ה- פאזר לבנות מתוך java.util.concurrent חֲבִילָה. זה מבנה דומה מאוד ל CountDownLatch המאפשר לנו לתאם ביצוע חוטים. בהשוואה ל CountDownLatch, יש לו פונקציונליות נוספת.

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

2. פאזר ממשק API

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

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

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

החוט מאותת שהגיע למחסום באמצעות קריאה ל arriveAndAwaitAdvance (), שזו שיטת חסימה. כאשר מספר המסיבות שהגיעו שווה למספר הצדדים הרשומים, ביצוע התוכנית יימשך, ומספר השלב יגדל. אנו יכולים לקבל את מספר השלב הנוכחי על ידי התקשרות ל- getPhase () שיטה.

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

3. יישום לוגיקה באמצעות פאזר ממשק API

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

ניצור a LongRunningAction כיתה המיישמת את ניתן לרוץ מִמְשָׁק:

מחלקה LongRunningAction מיישמת Runnable {private String threadName; Phaser ph פרטי; LongRunningAction (מחרוזת שם, Phaser ph) {this.threadName = threadName; this.ph = ph; ph.register (); } @ ביטול הפעלה בטלנית ציבורית () {ph.arriveAndAwaitAdvance (); נסה את {Thread.sleep (20); } לתפוס (InterruptedException e) {e.printStackTrace (); } ph.arriveAndDeregister (); }}

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

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

לאחר סיום העיבוד, החוט הנוכחי מבטל את הרישום על ידי קריאת ה- arriveAndDeregister () שיטה.

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

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

ExecutorService executorService = Executors.newCachedThreadPool (); Phaser ph = Phaser חדש (1); assertEquals (0, ph.getPhase ());

השלב לאחר האתחול שווה לאפס.

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

לאחר מכן, בואו נתחיל שלוש LongRunningAction חוטי פעולה, שיחכו על המכשול עד שנקרא arriveAndAwaitAdvance () שיטה מהחוט הראשי.

זכור שאותחזנו את שלנו פאזר עם 1 והתקשר להירשם() שלוש פעמים נוספות. עכשיו, שלושה חוטי פעולה הודיעו שהם הגיעו למחסום, אז עוד שיחה אחת של arriveAndAwaitAdvance () יש צורך - זה מהחוט הראשי:

executorService.submit (LongRunningAction חדש ("thread-1", ph)); executorService.submit (LongRunningAction חדש ("חוט -2", ph)); executorService.submit (LongRunningAction חדש ("thread-3", ph)); ph.arriveAndAwaitAdvance (); assertEquals (1, ph.getPhase ());

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

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

executorService.submit (LongRunningAction חדש ("thread-4", ph)); executorService.submit (LongRunningAction חדש ("thread-5", ph)); ph.arriveAndAwaitAdvance (); assertEquals (2, ph.getPhase ()); ph.arriveAndDeregister ();

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

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

זהו שלב 0 זהו שלב 0 זהו שלב 0 חוט הברגה -2 לפני פעולת ריצה ארוכה הברגה -1 לפני פעולת ריצה ארוכה הברגה -3 לפני פעולת ריצה ארוכה זהו שלב 1 שלב 1 הברגה -4 לפני זמן רב פעולת ריצה השחלת חוט 5 לפני פעולת ריצה ארוכה

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

4. מסקנה

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

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


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