JavaRush /בלוג Java /Random-HE /ריבוי שרשורים: מה עושות שיטות מחלקות חוטים

ריבוי שרשורים: מה עושות שיטות מחלקות חוטים

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

שיטת Thread.start().

נתחיל עם חזרה. כפי שאתה בוודאי זוכר, אתה יכול ליצור שרשור על ידי ירושה של המחלקה שלך מהמחלקה Threadועקוף השיטה בה run(). אבל, כמובן, זה לא יתחיל מעצמו. לשם כך, אנו קוראים למתודה על האובייקט שלנו start(). ריבוי שרשורים: מה עושות השיטות של מחלקת Thread - 2בואו נזכור את הדוגמה מההרצאה הקודמת:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Выполнен поток " + getName());
   }
}


public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {
           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
שימו לב: כדי לפתוח שרשור, עליכם לקרוא לשיטה המיוחדתstart(), לא ל-run()! זוהי טעות קלה לעשות, במיוחד כאשר לומדים לראשונה ריבוי שרשורים. אם בדוגמה שלנו אתה קורא למתודה של האובייקטrun()במקוםstart(), התוצאה תהיה כך:
public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {
           MyFirstThread thread = new MyFirstThread();
           thread.run();
       }
   }
}
שרשור-0 הוצא לפועל שרשור-1 שרשור הוצא לפועל שרשור-2 שרשור הופעל חוט-3 שרשור הופעל חוט-4 שרשור הוצא לפועל שרשור-6 שרשור בוצע. תסתכל על רצף הפלט: הכל הולך לפי הסדר. מוזר, נכון? אנחנו לא רגילים לזה, כי אנחנו כבר יודעים שסדר ההשקה והביצוע של השרשורים נקבע על ידי אינטליגנציה העל בתוך מערכת ההפעלה שלנו - מתזמן השרשורים. אולי פשוט היה לי מזל? כמובן, זה לא עניין של מזל. אתה יכול לאמת זאת על ידי הפעלת התוכנית עוד כמה פעמים. הנקודה היא שלקריאה ישירה למתודהrun()אין שום קשר לריבוי ההליכים. במקרה זה, התוכנית תבוצע בשרשור הראשי - זה שבו השיטה מבוצעתmain(). זה פשוט יוציא 10 שורות ברצף לקונסולה וזהו. לא יתחילו 10 שרשורים. לכן, זכרו לעתיד ובדקו את עצמכם כל הזמן. אם אתה רוצה שזה ייעשהrun(), התקשר לזהstart(). בוא נמשיך הלאה.

שיטת Thread.sleep().

כדי להשהות את ביצוע השרשור הנוכחי לזמן מה, השתמש ב- sleep(). ריבוי שרשורים: מה עושות השיטות של מחלקת Thread - 3השיטה sleep()לוקחת כפרמטר את מספר האלפיות השניות, כלומר, הזמן שבו צריך להשכיב את החוט.
public class Main {

   public static void main(String[] args) throws InterruptedException {

       long start = System.currentTimeMillis();

       Thread.sleep(3000);

       System.out.println(" - Сколько я проспал? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " секунды");

   }
}
פלט קונסולה: - כמה זמן ישנתי? - 3 שניות שימו לב: השיטה sleep()היא סטטית: היא משכימה את השרשור הנוכחי למצב שינה. כלומר, זה שעובד כרגע. ניואנס חשוב נוסף: ניתן להפריע לזרימה במצב שינה. במקרה זה, יתרחש חריג בתוכנית InterruptedException. נסתכל על דוגמה להלן. אגב, מה קורה אחרי שהשרשור "מתעורר"? האם היא תמשיך מיד בביצוע מהמקום שבו הפסיקה? לא. לאחר שהשרשור מתעורר - כשהזמן חלף כטיעון עד שפג Thread.sleep()- הוא נכנס למצב שניתן להרצה . עם זאת, זה לא אומר שמתזמן השרשור יפעיל אותו. בהחלט יתכן שהוא ייתן עדיפות לחוט "לא ישן" אחר, והחוט "התעורר טרי" שלנו ימשיך לעבוד קצת מאוחר יותר. הקפד לזכור: "להתעורר לא אומר להמשיך לעבוד באותה שנייה!"

שיטת Thread.join().

ריבוי שרשורים: מה עושות השיטות של מחלקת ה-Thread - 4השיטה join()משעה את ביצוע השרשור הנוכחי עד להשלמת שרשור אחר. אם יש לנו 2 שרשורים, t1ו- t2, ונכתוב -
t1.join()
t2לא יתחיל לעבוד עד ש-t1 סיים את עבודתו. join()ניתן להשתמש בשיטה כדי להבטיח את רצף הביצוע של שרשורים. בואו נסתכל על העבודה join()באמצעות דוגמה:
public class ThreadExample extends Thread {

   @Override
   public void run() {

       System.out.println("Начало работы потока " + getName());

       try {
           Thread.sleep(5000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println("Поток " + getName() +  " завершил работу.");
   }
}


public class Main {

   public static void main(String[] args) throws InterruptedException {

       ThreadExample t1 = new ThreadExample();
       ThreadExample t2 = new ThreadExample();

       t1.start();


 /*Второй поток t2 начнет выполнение только после того, How будет завершен
       (or бросит исключение) первый поток - t1*/
       try {
           t1.join();
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       t2.start();

       //Главный поток продолжит работу только после того, How t1 и t2 завершат работу
       try {
           t1.join();
           t2.join();
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       System.out.println("Все потоки закончor работу, программа завершена");

   }
}
יצרנו שיעור פשוט ThreadExample. המשימה שלו היא להציג הודעה על המסך על תחילת העבודה, לאחר מכן להירדם ל-5 שניות ובסיומה להודיע ​​על סיום העבודה. שום דבר מסובך. ההיגיון העיקרי נמצא בכיתה Main. תסתכל על ההערות: באמצעות השיטה, join()אנו שולטים בהצלחה ברצף הביצוע של השרשורים. אם אתה זוכר את תחילת הנושא, מתזמן השרשור עשה זאת. הוא השיק אותם לפי שיקול דעתו: בכל פעם אחרת. כאן, באמצעות השיטה, דאגנו שקודם ה-thread יופעל ויבוצע t1, לאחר מכן ה-thread t2, ורק אחריהם ה-thread הראשי של ביצוע התוכנית. לך על זה. בתוכניות אמיתיות, לעתים קרובות תתקל במצבים שבהם אתה צריך להפריע לביצוע של שרשור כלשהו. לדוגמה, השרשור שלנו פועל, אך הוא ממתין לאירוע או תנאי מסוים שיתקיים. אם זה קורה, זה מפסיק. זה כנראה יהיה הגיוני אם הייתה איזו שיטה כמו stop(). עם זאת, הכל לא כל כך פשוט. פעם, שיטה Thread.stop()ב-Java למעשה הייתה קיימת ואפשרה לך להפריע לעבודה של שרשור. אבל מאוחר יותר הוא הוסר מספריית ג'אווה. אתה יכול לחפש אותו בתיעוד של Oracle ולראות שהוא מסומן כמוצא משימוש . למה? כי זה פשוט עצר את הזרימה ללא כל עבודה נוספת. לדוגמה, שרשור יכול לעבוד עם נתונים ולשנות בו משהו. ואז הוא הודח בפתאומיות stop()באמצע העבודה - וזהו. לא היה כיבוי נכון, לא שחרור משאבים, אפילו לא טיפול בשגיאות - שום דבר מזה לא קרה. השיטה stop(), אם להגזים, פשוט הרסה את כל מה שנקרה בדרכה. ניתן להשוות את פעולתו לאופן שבו מישהו שולף את התקע מהשקע כדי לכבות מחשב. כן, אתה יכול להשיג את התוצאה הרצויה. אבל כולם מבינים שבעוד כמה שבועות המחשב לא יגיד "תודה" על זה. מסיבה זו, הלוגיקה של הפסקת שרשורים ב-Java שונה, וכעת נעשה שימוש בשיטה מיוחדת - interrupt().

שיטת Thread.interrupt().

מה קורה אם קוראים למתודה בשרשור interrupt() ? יש 2 אפשרויות:
  1. אם האובייקט היה במצב המתנה באותו רגע, למשל, joinאו sleep, ההמתנה תופסק והתוכנית תזרוק InterruptedException.
  2. אם השרשור היה במצב עבודה באותו רגע, הדגל הבוליאני של האובייקט יוגדר interrupted.
אבל נצטרך לבדוק את הערך של הדגל הזה באובייקט ולהשלים את העבודה בצורה נכונה בעצמנו! לשם כך, לכיתה Threadיש שיטה מיוחדת - boolean isInterrupted(). נחזור לדוגמא השעון מההרצאה במנה העיקרית. מטעמי נוחות, זה מעט מפושט:
public class Clock extends Thread {

   public static void main(String[] args) throws InterruptedException {
       Clock clock = new Clock();
       clock.start();

       Thread.sleep(10000);
       clock.interrupt();
   }

   public void run() {
       Thread current = Thread.currentThread();

       while (!current.isInterrupted())
       {
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               System.out.println("Работа потока была прервана");
               break;
           }
           System.out.println("Tik");
       }
   }
}
במקרה שלנו, השעון מתחיל ומתחיל לתקתק כל שנייה. בשנייה ה-10 אנו קוטעים את זרימת השעון. כפי שאתה כבר יודע, אם השרשור שאנו מנסים להפריע נמצא באחד ממצבי ההמתנה, זה גורם ל- InterruptedException. חריג מסוג זה הוא חריג מסומן, כך שניתן לתפוס אותו בקלות ולהפעיל את לוגיקת סיום התוכנית שלנו. זה מה שעשינו. הנה התוצאה שלנו: Tik Tik Tik Tik Tik Tik Tik Tik Tik עבודת השרשור נקטעה. זה מסיים את ההקדמה שלנו לשיטות העיקריות של הכיתה Thread. כדי לגבש את הידע שלך, אתה יכול לצפות בהרצאת וידאו זו על ריבוי הליכי שרשור:
זה ישמש כחומר נוסף מעולה! בסוף, לאחר סקירה של השיטות, הוא מספר בדיוק מה נעבור בהמשך הקורס :) בהצלחה!
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION