רק מי שמבין במחלקת Thread בג'אווה

מיכל202

New member
רק מי שמבין במחלקת Thread בג'אווה

בס"ד
התחלנו ללמוד את מחלקת Thread ואני די מתקשה בזה.
מה שלמשל אני לא מבינה זה מדוע משתמשים במתודת start ולא ישר במתודת run.

יש פה סיכום קצר בנושא באנגלית. אני מבינה אנגלית, מה שאני לא מבינה זה את הסיכום:

"The start() method, executes the run() method of new thread.
The run() method just executes in the current thread, without starting a new thread.
Also calling the run() method on the newly created Thread (Java Thread object) would block the execution of the parent thread till it completes. So that makes the intended parallel processing through two threads ( the new thread and the parent thread) actually serial.
Use start() to have the task run in parallel to the current task, and other tasks.
Don't use a Thread object's run() method. If you want to run the task serially in the current thread then don't use a Thread object at all, instead use a Runnable or any other normal method.
The proper use of Threads and threads is important. And understanding the difference between run() and start() is one of the most basic building blocks. You have to be able to know when things are meant to run in parallel (or 'concurrently') and when they are meant to run serially (or in the same thread)."

נתחיל משני המשפטים הראשונים. הוא אומר משהו כמו ש- start פועלת על thread חדש ו-run פועלת על thread נוכחי. אני לא מבינה את זה, הרי כל אובייקט אני מאתחלת ואז מפעילה עליו את start:
Thread t1 = new Thread("T1");
Thread t2 = new Thread("T2");
System.out.println("begin");
t1.start();
t2.start();
System.out.println("end");

הרי אני לא כותבת סתם start אלא t1.start זה לא אומר שהוא מופעל על ה- thread הנוכחי (הלוא הוא t1)?

שאלה שנייה: כבר קראתי הרבה ועדיין לא הבנתי מדוע את ה- "end" הוא לא מדפיס לי בסוף.
שאלה שלישית: אני מבינה שכל הקטע ב- Thread זה לגרום לפעילות לכאורה "סימולטנית" אבל על פי מה נקבעת הסימולטניות? אם במתודת ה- run שלי אני אומרת לו לישון אז אני מבינה, אבל אם לא - הרי אז לא אמור להתבצע הקוד לפי הסדר? כלומר קודם t1 אחר כך t2 ואחר כך להדפיס לי "end"? ובכלל, הדפסת ה- "end" אינה thread. האם "היריבות על הריצה" לא אמורה להתבצע רק בין פעולות על אובייקטים שהם thread, ופעולות שאינן על אובייקטים שהם thread אמורות להתבצע כרגיל במקום שכתבתי אותן?

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

תודה לעונים ולכל העוסקים במלאכה
 

nocgod

New member
ואו...המון שאלות :)

קודם כל אני חושב שהאתר הבא מסביר בצורה סבירה ++ את ההבדלים בין run() לבין start, ולמען האמת אני לא זוכר פעם אחת שעשיתי run...אני מעביר runnable לthread ותמיד עושה start.
בגדול כשאת עושה start לנים חדש הוא לא מתחיל ריצה אלא רק שם את ביצוע הthread ל queue כלשהו של המתזמן של הJVM. ברגע שמתזמן ה JVM בוחר את הthread שלך הוא גם עושה run. כלומר run זה ביצוע העבודה בתחלס כאשר start זה הכנסת העבודה לתור ביצוע.
כל נים אפשר להתחיל פעם אחת בלבד וברגע שהוא סיים את עבודתו אי אפשר לעשות לו restart צריך לייצר חדש ועל כן start אפשר לבצע אך ורק על נים חדש כי ניסיון לעשות זאת על נים שכבר הורץ יניב חריגה.

הנים הנוכחי איננו t1 הנים הנוכחי הוא הנים שמריץ את הconsole כביכול כאשר הנים t1 הוא נים ש"יוולד" יבצע את עבודתו ו"ימות" בלי קשר לנים הראשי ש"הוליד" אותו.

למה ה end לא מופיע בסוף -> ובכן בכלל שביצוע הנימים הראשי t1 t2 מתבצע בצורה מקבילה (ברוב המחשבים היום הוא באמת מתבצע בצורה מקבילה) אזי שקורה מצב של אי וודאות מתי יודפס ה end בפועל כי הכל תלוי במתזמן המשאבים (מי יקבל את המשאב הלא הוא הconsole שלך) כדי להדפיס על המסך.

הקומפיילר איננו מקצה זמן ריצה לנימים -> קומפיילר לוקח קוד בשפת ה JAVA ומתרגם אותו לbytecode אשר יותר מאוחר רץ על JVM.
זמן ריצה הנים נקבעת על ידי מנגנון שנקרא מתזמן Scheduler והוא זה שמחליט מי רץ מתי ולמה. לך לרוב אין שליטה על החלטות המתזמן אלא אם כן את מממשת מתזמן משלך שיעשה את זה בתוכנית שלך.
מה קורה בפועל? יש הרבה אלגוריתמי תיזמון שונים ומשונים החל מחלוקת זמן שווה ועד לחלוקת זמן על פי עדיפות הנים ביחס לנימים אחרים.

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

אם את לומדת במכללה/אוניברסיטה ויש לך דואר אלקטרוני שנגמר ב ac.il יש לך אופציה להרשם לאתר dreamspark ודרכו לקבל גישה של 3 חודשים חינם לספריה של pluralsight שהיא סיפריה מדהימה של המון הסברים על המון נושאים (זה לא יהפוך אותך לגאון הדור אבל מכיל מספיק מידע בשביל jump start טוב) נכון לעכשיו יש שם 6 קורסי ג'אווה... לדעתי שווה :)

אם יש לי טעויות אשמח לתיקון חבר'ה :) אני בכלל עושה asynctasks באנדרואיד...
 

nocgod

New member
מה זה גוגל?

JVM - Java Virtual Machine
קוד ג'אווה לכשעצמו לא רץ על הCPU ישירות, הוא מתורגם לשפת ביניים הנקראת bytecode בסוף תהליך הקומפילציה שלו, את קוד הbytecode מריץ הJVM אותו הוא מקמפל בזמן ריצה (תקרא Just in time) ומריץ אותו על המחשב. מעבר לזה פשוט תחפשי בגוגל ותמצאי הרחבות אם זה מעניין אותך
CPU - Central Processing Unit
יחידת עיבוד ראשית היא ה"לב" של המחשב, כל יחידה יודעת לבצע פקודות מסויימות מאוד, כל הקודים בעולם בסופו של דבר מתורגמים לפקודות האלה והמעבד מבצע אותן, כמוכן דואגים להעביר לCPU בנוסף לפקודות גם מידע אותו הוא צריך לעבד, המידע נטען מהזיכרון RAM לזיכרון המקומי של המעבד (registers) שם מתבצעות עליו פעולות והוא מוחזר לזיכרון.

מתזמן - מנגנון של מערכת ההפעלה אשר מתזמן פעילויות של Threads במקרה והמערכת הפעלה מכירה בהם, או מתזמן פעילויות של Processes (והם מנהלים את התזמון של הThreads...)

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

מיכל202

New member
תודה רבה


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

nocgod

New member
המחשה לריצה מקבילה

public class Driver
{
public static void main(String[] args)
{
System.out.println("Thread#: " + Thread.currentThread().getId() + ": Start");
Thread t1 = new Thread(new Runnable()
{
public void run()
{
for (int i = 0; i < 100; i++)
System.out.println(" >> Thread# " + Thread.currentThread().getId() + ": " + i);
}
});
Thread t2 = new Thread(new Runnable()
{
public void run()
{
for (int i = 0; i < 100; i++)
System.out.println(" >> Thread# " + Thread.currentThread().getId() + ": " + i);
}
});
t1.start();
t2.start();
System.out.println("Thread#: " + Thread.currentThread().getId() + ": End");
}
}


אני עושה 2 נימים חוץ מהנים הראשי. הנים הראשי מדפיס את ה ID שלו ואומר start ואז מאתחל 2 נימים ועושה להם start ואז מדפיס שוב את הID שלו ומדפיס end

כל אחד מהנימים עושה עבודה פשוטה לולאה מ 0 עד 100 וכל פעם מדפיס את ה ID שלו ואת ערך i.
שימי לב שstart מודפס ראשון וישר אחריו end (אבל זה לא יהיה כך תמיד, זה תלוי במתזמן!!!) אבל שימי לב שלא כל נים מקבל את כל זמן המעבד כל הזמן, פעם מקבל נים אחד והוא מדפיס על המסך ופעם מקבל נים אחר.
תעתיקי את זה ל eclipse ותריצי...
 

nocgod

New member
המחשה למה קורה כשעושים run

לאחר שהרצת את הקוד הקודם אני רוצה שתחליפי את פקודת ה start שאני מפעיל על כל נים בפקודת run ותראי על איזה נים מתבצעת העבודה. מוזר לא?
מאחר ולא התחלת את הנים אלא רק הרצת קוד הקוד רץ על הנים היחיד הפנוי הלא הוא הנים הראשי. בגלל שלא קראת ל start לתזמון העבודה על נים חדש.
 
למעלה