Java 101: הבנת שרשורי Java, חלק 4: קבוצות חוטים, תנודתיות ומשתנים מקומיים של שרשור

Java 101 החודש מסכם את סדרת השרשור על ידי התמקדות בקבוצות שרשור, תנודתיות, משתנים מקומיים של שרשור, טיימרים ושיעור ThreadDeath.

הבנת נושאי Java - קרא את כל הסדרה

  • חלק 1: הצגת חוטים ורצים
  • חלק 2: סנכרון חוטים
  • חלק 3: תזמון חוטים, המתנה / הודעה והפרעה לשרשור
  • חלק 4: קבוצות חוטים, תנודתיות, משתנים מקומיים של חוטים, טיימרים ומוות חוטים

קבוצות חוטים

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

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

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

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

מתחת mainהקבוצה מתגורר נמצאת subgroup 1ואת subgroup 2תת קבוצות, תת-קבוצות שנוצרו ביישום (אשר היישום של הדמות יוצרת). יתר על כן, subgroup 1שלוש קבוצות נושאים שנוצר יישום: thread 1, thread 2, ו thread 3. לעומת זאת, subgroup 2אחת הקבוצות יישום שנוצר חוט: my thread.

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

צור קבוצות שרשור ושייך שרשורים לקבוצות אלה

ThreadGroupתיעוד SDK של הכיתה מגלה שני בנאים: ThreadGroup(String name)ו ThreadGroup(ThreadGroup parent, String name). שני הבונים יוצרים קבוצת חוטים ונותנים לה שם, כפי nameשמציין הפרמטר. הבונים נבדלים בבחירתם באיזו קבוצת חוטים משמשת הורה לקבוצת החוטים שזה עתה נוצרה. כל קבוצת שרשורים, למעט system, חייבת לכלול קבוצת שרשור הורה. שכן ThreadGroup(String name), ההורה הוא קבוצת החוטים של השרשור שמתקשר ThreadGroup(String name). לדוגמא, אם החוט הראשי מתקשר ThreadGroup(String name), לקבוצת החוטים החדשה שנוצרה יש את הקבוצה של השרשור הראשי כהורה שלה - main. שכן ThreadGroup(ThreadGroup parent, String name), ההורה הוא הקבוצה parentשמפנה אליה. הקוד הבא מראה כיצד להשתמש בבונים אלה ליצירת זוג קבוצות חוטים:

public static void main (String [] args) { ThreadGroup tg1 = new ThreadGroup ("A"); ThreadGroup tg2 = new ThreadGroup (tg1, "B"); }

בקוד לעיל, השרשור הראשי יוצר שתי קבוצות שרשור: Aו- B. ראשית, החוט המרכזי יוצר על Aידי התקשרות ThreadGroup(String name). tg1האם של חוט קבוצת -referenced הוא mainכי mainהיא קבוצת החוט של החוט העיקרי. שנית, החוט המרכזי יוצר על Bידי התקשרות ThreadGroup(ThreadGroup parent, String name). tg2האם של חוט קבוצת -referenced הוא Aכי tg1ההתייחסות של" עוברת כארגומנט ThreadGroup (tg1, "B")ו Aמקורבת עם tg1.

טיפ: ברגע שאתה כבר לא צריך היררכיה של ThreadGroupאובייקטים, שיחה ThreadGroupשל void destroy()שיטה באמצעות התייחסות ThreadGroupהאובייקט בראש ההיררכיה. אם ThreadGroupלאובייקט העליון ולכל אובייקטים destroy()מקבוצת המשנה חסרים אובייקטים של חוטים, מכין אותם אובייקטים של קבוצת חוטים לאיסוף אשפה. אחרת, destroy()זורק IllegalThreadStateExceptionחפץ. עם זאת, עד שתבטל את ההתייחסות ThreadGroupלאובייקט העליון (בהנחה שמשתנה שדה מכיל את ההפניה), אספן הזבל אינו יכול לאסוף את האובייקט הזה. הפניה לאובייקט העליון, אתה יכול לקבוע אם שיחה קודמת נעשתה על destroy()השיטה על ידי הקורא ThreadGroupשל" boolean isDestroyed()שיטה. שיטה זו מחזירה אמת אם היררכיית קבוצת החוטים הושמדה.

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

ThreadGroup tg = new ThreadGroup ("subgroup 2"); Thread t = new Thread (tg, "my thread");

הקוד שלמעלה יוצר תחילה subgroup 2קבוצה עם mainקבוצת ההורים. (אני מניח שהחוט הראשי מבצע את הקוד.) הקוד הבא יוצר my threadThreadאובייקט subgroup 2בקבוצה.

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

רישום 1. ThreadGroupDemo.java

// ThreadGroupDemo.java class ThreadGroupDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("subgroup 1"); Thread t1 = new Thread (tg, "thread 1"); Thread t2 = new Thread (tg, "thread 2"); Thread t3 = new Thread (tg, "thread 3"); tg = new ThreadGroup ("subgroup 2"); Thread t4 = new Thread (tg, "my thread"); tg = Thread.currentThread ().getThreadGroup (); int agc = tg.activeGroupCount (); System.out.println ("Active thread groups in " + tg.getName () + " thread group: " + agc); tg.list (); } }

ThreadGroupDemoיוצר את קבוצת החוטים המתאימה וחפצי הברגה כדי לשקף את מה שאתה רואה באיור לעיל. כדי להוכיח כי subgroup 1ואת subgroup 2הקבוצות הן mainקבוצות משנה רק ים", ThreadGroupDemoעושה את הדברים הבאים:

  1. מאחזר התייחסות של החוט הראשי ThreadGroupאובייקט על ידי קורא Thread'סטטי של currentThread()השיטה (אשר מחזירה הפניה של החוט העיקרי Threadהאובייקט) ואחריו Threadשל ThreadGroup getThreadGroup()השיטה.
  2. השיטה ThreadGroupשל שיחות בהפניה int activeGroupCount()שהוחזרה זה עתה ThreadGroupלהחזרת אומדן של קבוצות פעילות בתוך קבוצת השרשור הראשי.
  3. השיטה ThreadGroupשל שיחות String getName ()להחזיר את שם קבוצת החוטים של החוט הראשי.
  4. השיטה ThreadGroupשל שיחות void list ()להדפיס על פרטי התקן הפלט הסטנדרטי בקבוצת החוטים של השרשור הראשי ובכל קבוצות המשנה.

כאשר הוא מופעל, ThreadGroupDemoמציג את הפלט הבא:

Active thread groups in main thread group: 2 java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] Thread[Thread-0,5,main] java.lang.ThreadGroup[name=subgroup 1,maxpri=10] Thread[thread 1,5,subgroup 1] Thread[thread 2,5,subgroup 1] Thread[thread 3,5,subgroup 1] java.lang.ThreadGroup[name=subgroup 2,maxpri=10] Thread[my thread,5,subgroup 2]

פלט שמתחיל עם Threadתוצאות list()"שיחות פנימיות של אל Thread" s toString()השיטה, פורמט פלט שתיארתי בחלק 1. יחד עם פלט כי, אתה רואה פלט החל java.lang.ThreadGroup. פלט זה מזהה את שם קבוצת החוטים ואחריו העדיפות המקסימלית שלה.

קבוצות עדיפות וחוטים

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

Java מקצה עדיפות מקסימלית לכל קבוצת שרשורים. כשאתה יוצר קבוצה, Java משיגה עדיפות זו מקבוצת האם שלה. השתמשו ThreadGroupשל void setMaxPriority(int priority)שיטה מכן קבע את העדיפות מקסימלית. לאשכולות שתוסיף לקבוצה לאחר הגדרת העדיפות המקסימלית שלה לא תהיה עדיפות העולה על המקסימום. כל שרשור עם עדיפות גבוהה יותר יורד אוטומטית כאשר הוא מצטרף לקבוצת השרשור. עם זאת, אם אתה משתמש setMaxPriority(int priority)בהורדת העדיפות המקסימלית של קבוצה, כל האשכולות שנוספו לקבוצה לפני אותה קריאה לשיטה ישמרו על סדר העדיפויות המקורי. לדוגמה, אם תוסיף עדיפות 8 של עדיפות לקבוצת עדיפות מקסימאלית 9 ואז תנמיך את העדיפות המקסימלית של אותה קבוצה ל- 7, נושא העדיפות 8 נשאר בעדיפות 8. בכל עת תוכל לקבוע קבוצת אשכולות 'זה עדיפות מקסימלית על ידי התקשרות ThreadGroupשלint getMaxPriority()שיטה. כדי להדגים עדיפות וקבוצות שרשור, כתבתי MaxPriorityDemo:

רישום 2. MaxPriorityDemo.java

// MaxPriorityDemo.java class MaxPriorityDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("A"); System.out.println ("tg maximum priority = " + tg.getMaxPriority ()); Thread t1 = new Thread (tg, "X"); System.out.println ("t1 priority = " + t1.getPriority ()); t1.setPriority (Thread.NORM_PRIORITY + 1); System.out.println ("t1 priority after setPriority() = " + t1.getPriority ()); tg.setMaxPriority (Thread.NORM_PRIORITY - 1); System.out.println ("tg maximum priority after setMaxPriority() = " + tg.getMaxPriority ()); System.out.println ("t1 priority after setMaxPriority() = " + t1.getPriority ()); Thread t2 = new Thread (tg, "Y"); System.out.println ("t2 priority = " + t2.getPriority ()); t2.setPriority (Thread.NORM_PRIORITY); System.out.println ("t2 priority after setPriority() = " + t2.getPriority ()); } }

בעת הפעלה, MaxPriorityDemoמייצר את הפלט הבא:

tg maximum priority = 10 t1 priority = 5 t1 priority after setPriority() = 6 tg maximum priority after setMaxPriority() = 4 t1 priority after setMaxPriority() = 6 t2 priority = 4 t2 priority after setPriority() = 4

קבוצת האשכולות A( tgשמפנה אליה ) מתחילה בעדיפות הגבוהה ביותר (10) כמקסימום. שרשור X, Threadשהאובייקט שלו t1מתייחס אליו, מצטרף לקבוצה ומקבל 5 בראש סדר העדיפויות. אנו משנים את העדיפות של השרשור ההוא ל -6, מה שמצליח מכיוון ש- 6 הוא פחות מ 10. לאחר מכן, אנו קוראים setMaxPriority(int priority)להפחית את העדיפות המקסימלית של הקבוצה ל -4. למרות שהחוט Xנשאר בעדיפות 6, Yשרשור חדש שנוסף מקבל 4 כעדיפותו. לבסוף, ניסיון להגדיל Yאת עדיפות החוט ל -5 נכשל מכיוון ש -5 גדול מ -4.

הערה:setMaxPriority(int priority) מתאים באופן אוטומטי את העדיפות המקסימלית של קבוצות המשנה של קבוצת החוטים.

בנוסף לשימוש בקבוצות שרשור כדי להגביל את עדיפות השרשור, תוכל לבצע משימות אחרות על ידי קריאה ThreadGroupלשיטות שונות החלות על שרשור כל קבוצה. שיטות כוללות void suspend(), void resume(), void stop(), ו void interrupt(). מכיוון ש- Sun Microsystems ביטלה את שלושת השיטות הראשונות (הן אינן בטוחות), אנו בוחנים רק interrupt().

קוטע קבוצת חוטים

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

רישום 3. InterruptThreadGroup.java

// InterruptThreadGroup.java class InterruptThreadGroup { public static void main (String [] args) { MyThread mt = new MyThread (); mt.setName ("A"); mt.start (); mt = new MyThread (); mt.setName ("B"); mt.start (); try { Thread.sleep (2000); // Wait 2 seconds } catch (InterruptedException e) { } // Interrupt all methods in the same thread group as the main // thread Thread.currentThread ().getThreadGroup ().interrupt (); } } class MyThread extends Thread { public void run () { synchronized ("A") { System.out.println (getName () + " about to wait."); try { "A".wait (); } catch (InterruptedException e) { System.out.println (getName () + " interrupted."); } System.out.println (getName () + " terminating."); } } }