JavaRush /בלוג Java /Random-HE /דוגמה SynchronousQueue ב-Java - פתרון הבעיה Producer Cons...
profeg
רָמָה

דוגמה SynchronousQueue ב-Java - פתרון הבעיה Producer Consumer

פורסם בקבוצה
דוגמה SynchronousQueue ב-Java - פתרון הבעיה Producer Consumer
SynchronousQueue הוא סוג מיוחד של BlockingQueue שבו כל פעולת הוספה חייבת לחכות לפקודת הסרה מתאימה בשרשור אחר, ולהיפך. כאשר אתה קורא לשיטת put() ב- SynchronousQueue, הוא חוסם עד שרשור אחר לוקח ממנו את האלמנט הזה. בהתאם לכך, אם פתיל אחר מנסה להסיר אלמנט ממנו, והאלמנט אינו שם, אז פתיל זה חוסם עד שהשרשור השני מכניס את האלמנט לתור. אפשר לחשוב על SynchronousQueue כעל ספורטאי ( חוט ) שרץ עם הלפיד האולימפי, הוא רץ עם הלפיד (החפץ שמועבר הלאה) ומעביר אותו לספורטאי אחר שמחכה בצד השני. אם תשים לב לשם, תבין ש-SynchronousQueue נקרא כך מסיבה כלשהי: הוא מעביר נתונים באופן סינכרוני לשרשור אחר ; הוא ממתין שמישהו יאסוף את הנתונים במקום פשוט להכניס אותם ולצאת (פעולה אסינכרונית). אם אתה מכיר את CSP ואת Ada, אז אתה יודע שתורים מסונכרנים דומים למפגש של שרשורים. הם מתאימים היטב למבני העברת בקרה שבהם אובייקט הפועל בשרשור אחד חייב להסתנכרן עם אובייקט בשרשור אחר כדי להעביר אליו מידע, אירוע או משימה כלשהי. במדריכים קודמים של תכנות מרובי-שרשורים, למדנו כיצד לפתור את בעיית היצרן-צרכן באמצעות שיטות המתנה והודעה ו- BlockingQueue . כעת נלמד כיצד ליישם את דפוס היצרן-צרכן באמצעות SynchronousQueue. מחלקה זו תומכת בנוסף בהתנהגות הוגנת להזמנת המתנה של שרשורי היצרן והצרכנים. כברירת מחדל, הזמנה זו אינה מובטחת. עם זאת, תורים שנוצרו עם מאפיינים הוגנים מבטיחים גישה לשרשורים בתור FIFO (Firs In First Out).
מפיק/צרכן באמצעות SynchronousQueue ב-Java.
דוגמה SynchronousQueue ב-Java - פתרון הבעיה Producer Consumer - 1 כפי שאמרתי למעלה, אין דבר טוב יותר מבעיית יצרן-צרכן להבנת תקשורת בין חוטים בכל שפת תכנות. בבעיה זו, חוט אחד משמש כמפיק שמפיק אירועים ומשימות, והשרשור השני משמש כצרכן שלו. מאגר משותף משמש להעברת נתונים מיצרן לצרכן. הקושי לפתור בעיה זו מגיע במקרים קיצוניים, למשל, כאשר היצרן נאלץ להמתין כי... המאגר מלא או שהצרכן נאלץ לחכות בגלל המאגר ריק. זה נפתר בקלות, כי... תור החסימה סיפק לא רק מאגר לאחסון נתונים, אלא גם בקרת זרימה, חסימת השרשור הקורא לשיטת put() (Producer) אם המאגר היה מלא, וחסימת השרשור הקורא לשיטת take() (Consumer) אם המאגר היה ריק. כעת נפתור את אותה בעיה באמצעות SynchronousQueue, סוג מיוחד של אוספים מקבילים עם קיבולת אפסית. בדוגמה הבאה יש לנו שני שרשורים שנקראים PRODUCER ו- CONSUMER (תמיד תן שמות לשרשורים, זה סגנון טוב מאוד של תכנות מרובה-שרשורים). השרשור הראשון מפרסם את הניקוד במשחק, והשרשור השני מכלה אותו. הציון במשחק הוא לא יותר מאובייקט מסוג String. אבל אם תפעיל את התוכנית עם סוג אחר, לא תבחין בהבדל. כדי להבין איך SynchronousQueue עובד, וכיצד לפתור את בעיית יצרן-צרכן, עליך: או להפעיל את התוכנית לניפוי באגים (debug) בסביבת Eclipse , או פשוט להתחיל את שרשור המפיק על ידי הערה ב-consumer.start(); אם שרשור הצרכן אינו פועל אז שרשור היצרן ייחסם ב-queue.put(event); אם פועל, לא תוכל לראות את המפיק [PRODUCER] מפרסם את אירוע :FOUR. זה קורה בגלל התנהגות ספציפית של SynchronousQueue, המבטיחה שנתוני פרסום השרשור ייחסמו עד שרשור אחר ייקח את הנתונים, ולהיפך. אתה יכול לבדוק את שאר הקוד על ידי הערת producer.start(); ומתחיל רק את חוט הצרכן. אם תלמדו היטב את תפוקות התוכנית, תבחינו שסדר הפלט הפוך. נראה שהשרשור [CONSUMER] לקח את הנתונים לפני שרשור [PRODUCER] הפיק אותם. הסיבה לכך היא SynchronousQueue לא מבטיח עמידה בתור כברירת מחדל. אבל יש לזה כללי הוגנות שקובעים את הגישה לשרשורים לפי סדר FIFO. אתה יכול להפעיל את הכללים האלה על ידי העברת נאמן לבנאי SynchronousQueue העמוס בצורה הבאה: import java.util.concurrent.SynchronousQueue; /** * Java Program to solve Producer Consumer problem using SynchronousQueue. A * call to put() will block until there is a corresponding thread to take() that * element. * * @author Javin Paul */ public class SynchronousQueueDemo{ public static void main(String args[]) { final SynchronousQueue queue = new SynchronousQueue (); Thread producer = new Thread("PRODUCER") { public void run() { String event = "FOUR"; try { queue.put(event); // thread will block here System.out.printf("[%s] published event : %s %n", Thread .currentThread() .getName(), event); } catch (InterruptedException e) { e.printStackTrace(); } } }; producer.start(); // starting publisher thread Thread consumer = new Thread("CONSUMER") { public void run() { try { String event = queue.take(); // thread will block here System.out.printf("[%s] consumed event : %s %n", Thread .currentThread() .getName(), event); } catch (InterruptedException e) { e.printStackTrace(); } } }; consumer.start(); // starting consumer thread } } Output: [CONSUMER] consumed event : FOUR [PRODUCER] published event : FOUR new SynchronousQueue(boolean fair).
מה שאתה צריך לזכור על SynchronousQueue ב-Java.

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

  1. SynchronousQueue חוסם, ועד שרשור אחד יהיה מוכן לקחת נתונים, אחר ינסה לשים נתונים.
  2. ל- SynchronousQueue אין נפח. כלומר, הוא אינו מכיל נתונים.
  3. SynchronousQueue משמש ליישום אסטרטגיית תור קדימה, כאשר שרשור מעביר שליטה לשרשור ממתין, או יוצר חדש אם מותר, אחרת הבקרה לא מועברת.
  4. תור זה אינו מאפשר נתונים null. ניסיון להוסיף אלמנט null יזרוק NullPointerException .
  5. אם אתה משתמש בשיטות אחרות מאוסף (כגון מכיל), SynchronousQueue מתנהג כמו אוסף ריק.
  6. אתה לא יכול להשתמש בשיטת ההצצה של SynchronousQueue מכיוון שהאלמנט קיים רק כאשר אתה מנסה להסיר אותו; כמו כן, לא תוכל להכניס אלמנטים (בכל שיטה) עד שרשור אחר ינסה להסיר אותו.
  7. לא תוכל להשתמש באיטרטור עבור SynchronousQueue כי... אין לו אלמנטים.
  8. ניתן ליצור SynchronousQueue עם כללים הוגנים, שבהם הגישה לשרשורים מובטחת בסדר FIFO.
אולי זה הכל על SynchronousQueue ב-Java. הסתכלנו על כמה מהמאפיינים המיוחדים של אוסף רב-הליכי זה, ולמדנו כיצד לפתור את הבעיה הקלאסית של יצרן-צרכן באמצעות SynchronousQueue ב-Java. אגב, לקרוא לזה תור זה לא לגמרי נכון, כי... הוא אינו מכיל אלמנטים. הקריאה ל-put() לא תושלם עד ל-take(). נכון יותר לחשוב על זה כעל מקום מפגש של חוטים, שבו הם חולקים אובייקט. במילים אחרות, זהו כלי עזר להעברת אובייקטים מסונכרנת ב-Java, אולי חלופה בטוחה יותר לשיטת ההמתנה וההודעה .
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION