JavaRush /בלוג Java /Random-HE /סנכרון שרשור. מפעיל מסונכרן ב-Java

סנכרון שרשור. מפעיל מסונכרן ב-Java

פורסם בקבוצה
שלום! היום נמשיך לשקול את המאפיינים של תכנות מרובה הליכי ונדבר על סנכרון חוטים.
סנכרון שרשור.  מפעיל מסונכרן - 1
מה זה "סנכרון"? מחוץ לתחום התכנות, זה מתייחס לסוג כלשהו של הגדרה המאפשרת לשני מכשירים או תוכניות לעבוד יחד. כך למשל, ניתן לסנכרן סמארטפון ומחשב עם חשבון גוגל, וחשבון אישי באתר ניתן לסנכרן עם חשבונות ברשתות חברתיות על מנת להיכנס באמצעותם. לסנכרון שרשור יש משמעות דומה: הוא מגדיר את האופן שבו חוטים מתקשרים זה עם זה. בהרצאות קודמות, החוטים שלנו חיו ופעלו בנפרד זה מזה. אחד סופר משהו, השני ישן, השלישי הציג משהו בקונסולה, אבל הם לא קיימו אינטראקציה זה עם זה. בתוכניות אמיתיות מצבים כאלה הם נדירים. מספר שרשורים יכולים לעבוד באופן פעיל, למשל, עם אותה סט נתונים ולשנות משהו בו. זה יוצר בעיות. תאר לעצמך ששרשורים מרובים כותבים טקסט לאותו מיקום, כגון קובץ טקסט או המסוף. הקובץ או המסוף הזה הופך במקרה זה למשאב משותף. שרשורים לא יודעים על קיומו של זה, אז הם פשוט רושמים את כל מה שהם יכולים לנהל בזמן שמתזמן השרשור מקצה להם. בהרצאה שנערכה לאחרונה בקורס, הייתה לנו דוגמה למה זה יוביל, בואו נזכור את זה: סנכרון שרשור.  מפעיל מסונכרן - 2הסיבה נעוצה בעובדה שהשרשורים עבדו עם משאב משותף, הקונסולה, מבלי לתאם פעולות זה עם זה. אם מתזמן השרשור הקצה זמן ל-Thread-1, הוא כותב הכל באופן מיידי לקונסולה. מה שרשורים אחרים כבר הספיקו לכתוב או לא הצליחו לכתוב זה לא חשוב. התוצאה, כפי שאתה יכול לראות, היא הרת אסון. לכן, בתכנות מרובה הליכי, הוצג מושג מיוחד mutex (מהאנגלית "mutex", "הדרה הדדית" - "הדרה הדדית") . המטרה של mutex היא לספק מנגנון כך שלחוט אחד בלבד תהיה גישה לאובייקט בזמן מסוים. אם Thread-1 רכש את המוטקס של אובייקט A, לשרשורים אחרים לא תהיה גישה אליו כדי לשנות בו שום דבר. עד שהמוטקס של אובייקט A ישתחרר, החוטים הנותרים ייאלצו להמתין. דוגמה לחיים האמיתיים: דמיינו שאתם ועוד 10 זרים משתתפים באימון. אתה צריך בתורו להביע רעיונות ולדון במשהו. אבל, מכיוון שאתם מתראים זה את זה בפעם הראשונה, כדי לא להפריע זה לזה כל הזמן ולא לרדת להמולה, אתם משתמשים בכלל ה"כדור מדבר": רק אדם אחד יכול לדבר - זה שיש לו את הכדור. הידיים שלו. כך הדיון מתברר כמספק ופורה. אז, מוטקס, במהותו, הוא כדור כזה. אם המוטקס של אובייקט נמצא בידיו של חוט אחד, חוטים אחרים לא יוכלו לגשת לאובייקט. אתה לא צריך לעשות שום דבר כדי ליצור mutex: זה כבר מובנה במחלקה Object, מה שאומר שלכל אובייקט ב-Java יש את זה.

כיצד פועל המפעיל המסונכרן ב-Java

בואו להכיר מילת מפתח חדשה - מסונכרנת . זה מסמן חלק מסוים מהקוד שלנו. אם בלוק קוד מסומן במילת המפתח המסונכרנת, זה אומר שניתן לבצע את הבלוק רק על ידי שרשור אחד בכל פעם. ניתן ליישם סנכרון בדרכים שונות. לדוגמה, צור שיטה מסונכרנת שלמה:
public synchronized void doSomething() {

   //...method logic
}
או כתוב גוש קוד שבו מתבצע סנכרון על אובייקט כלשהו:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
המשמעות פשוטה. אם שרשור אחד מזין גוש קוד המסומן במילה מסונכרנת, הוא רוכש מיד את המוטקס של האובייקט, וכל שאר השרשורים שמנסים להיכנס לאותו בלוק או שיטה נאלצים להמתין עד שהשרשור הקודם יסיים את עבודתו וישחרר את לפקח. סנכרון שרשור.  מפעיל מסונכרן - 3דרך אגב! בהרצאות הקורס כבר ראית דוגמאות לסנכרון, אבל הן נראו אחרת:
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
הנושא חדש עבורכם, וכמובן שבהתחלה יהיה בלבול עם התחביר. לכן, זכרו מיד כדי לא להתבלבל בהמשך שיטות הכתיבה. שתי שיטות הכתיבה הללו אומרות את אותו הדבר:
public void swap() {

   synchronized (this)
   {
       //...method logic
   }
}


public synchronized void swap() {

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

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
כפי שאתה יכול לראות, חלק מהשיטה מכיל היגיון שעבורו לא נדרש סנכרון. הקוד בו יכול להתבצע על ידי מספר שרשורים בו זמנית, וכל המקומות הקריטיים מוקצים לבלוק מסונכרן נפרד. ורגע אחד. בואו נסתכל מתחת למיקרוסקופ על הדוגמה שלנו מההרצאה עם חילופי השמות:
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
שימו לב: הסנכרון מתבצע באמצעות this. כלומר, עבור אובייקט ספציפי MyClass. תאר לעצמך שיש לנו 2 חוטים ( Thread-1ו Thread-2) ורק אובייקט אחד MyClass myClass. במקרה זה, אם Thread-1השיטה נקראת myClass.swap(), המוטקס של האובייקט יהיה תפוס, וכשתנסו Thread-2לקרוא לו, myClass.swap()הוא יתקע בהמתנה עד שהמוטקס יהפוך לחופשי. אם יש לנו 2 פתילים ו 2 אובייקטים MyClass- myClass1ו- myClass2על אובייקטים שונים, ה-threads שלנו יכולים בקלות לבצע שיטות מסונכרנות בו זמנית. השרשור הראשון עושה:
myClass1.swap();
השני עושה:
myClass2.swap();
במקרה זה, מילת המפתח המסונכרנת בתוך השיטה swap()לא תשפיע על פעולת התוכנית, מכיוון שהסנכרון מתבצע על אובייקט ספציפי. ובמקרה האחרון יש לנו 2 אובייקטים ולכן החוטים לא יוצרים בעיות אחד לשני. אחרי הכל, לשני אובייקטים יש 2 מוטקסים שונים, ולכידתם אינה תלויה זה בזה.

תכונות סנכרון בשיטות סטטיות

אבל מה אם אתה צריך לסנכרן שיטה סטטית?
class MyClass {
   private static String name1 = "Olya";
   private static String name2 = "Lena";

   public static synchronized void swap() {
       String s = name1;
       name1 = name2;
       name2 = s;
   }

}
לא ברור מה ישמש מוטקס במקרה זה. הרי כבר החלטנו שלכל חפץ יש מוטקס. אבל הבעיה היא שכדי לקרוא למתודה סטטית MyClass.swap()אנחנו לא צריכים אובייקטים: השיטה היא סטטית! אז מה הלאה? :/ למעשה, אין שום בעיה עם זה. יוצרי ג'אווה דאגו להכל :) אם השיטה המכילה את הלוגיקה הקריטית של "רב-חודים" היא סטטית, הסנכרון יתבצע לפי מחלקות. לבהירות רבה יותר, ניתן לשכתב את הקוד לעיל כ:
class MyClass {
   private static String name1 = "Olya";
   private static String name2 = "Lena";

   public static void swap() {

       synchronized (MyClass.class) {
           String s = name1;
           name1 = name2;
           name2 = s;
       }
   }

}
באופן עקרוני, יכולת לחשוב על זה בעצמך: מכיוון שאין אובייקטים, אז מנגנון הסנכרון חייב להיות איכשהו "מחובר" לשיעורים עצמם. ככה זה: אפשר גם לסנכרן בין שיעורים.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION