JavaRush /จาวาบล็อก /Random-TH /การซิงโครไนซ์เธรด ตัวดำเนินการซิงโครไนซ์ใน Java

การซิงโครไนซ์เธรด ตัวดำเนินการซิงโครไนซ์ใน Java

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพิจารณาคุณสมบัติของการเขียนโปรแกรมแบบมัลติเธรดต่อไปและพูดคุยเกี่ยวกับการซิงโครไนซ์เธรด
การซิงโครไนซ์เธรด  ตัวดำเนินการซิงโครไนซ์ - 1
“การซิงโครไนซ์” คืออะไร? ภายนอกขอบเขตการเขียนโปรแกรม หมายถึงการตั้งค่าบางประเภทที่ช่วยให้อุปกรณ์หรือโปรแกรมสองตัวทำงานร่วมกันได้ ตัวอย่างเช่น สมาร์ทโฟนและคอมพิวเตอร์สามารถซิงโครไนซ์กับบัญชี Google และบัญชีส่วนตัวบนเว็บไซต์สามารถซิงโครไนซ์กับบัญชีบนโซเชียลเน็ตเวิร์กเพื่อเข้าสู่ระบบโดยใช้บัญชีเหล่านั้น การซิงโครไนซ์เธรดมีความหมายคล้ายกัน: เป็นการตั้งค่าวิธีที่เธรดโต้ตอบระหว่างกัน ในการบรรยายครั้งก่อน หัวข้อของเราอาศัยและทำงานแยกจากกัน คนหนึ่งกำลังนับอะไรบางอย่าง คนที่สองกำลังนอนหลับ คนที่สามกำลังแสดงอะไรบางอย่างบนคอนโซล แต่พวกเขาไม่ได้โต้ตอบกัน ในโปรแกรมจริงสถานการณ์เช่นนี้เกิดขึ้นได้ยาก หลายเธรดสามารถทำงานได้ เช่น ใช้ชุดข้อมูลเดียวกันและเปลี่ยนแปลงบางอย่างในนั้น สิ่งนี้สร้างปัญหา ลองจินตนาการว่ามีหลายเธรดกำลังเขียนข้อความไปยังตำแหน่งเดียวกัน เช่น ไฟล์ข้อความหรือคอนโซล ไฟล์หรือคอนโซลในกรณีนี้จะกลายเป็นทรัพยากรที่ใช้ร่วมกัน Threads ไม่ทราบเกี่ยวกับการมีอยู่ของกันและกัน ดังนั้นพวกเขาจึงเพียงเขียนทุกสิ่งที่สามารถจัดการได้ในเวลาที่ตัวกำหนดตารางเวลาเธรดจัดสรรให้พวกเขา ในการบรรยายล่าสุดของหลักสูตรนี้ เรามีตัวอย่างว่าสิ่งนี้จะนำไปสู่อะไร โปรดจำไว้: การซิงโครไนซ์เธรด  ตัวดำเนินการซิงโครไนซ์ - 2เหตุผลก็คือความจริงที่ว่าเธรดทำงานกับทรัพยากรที่ใช้ร่วมกัน นั่นคือคอนโซล โดยไม่ได้ประสานการกระทำระหว่างกัน หากตัวกำหนดเวลาเธรดจัดสรรเวลาให้กับ Thread-1 ตัวกำหนดตารางเวลาจะเขียนทุกอย่างลงในคอนโซลทันที เธรดอื่นๆ ที่สามารถเขียนได้แล้วหรือยังไม่ได้เขียนนั้นไม่สำคัญ อย่างที่คุณเห็นผลลัพธ์ที่ได้คือหายนะ ดังนั้นในการเขียนโปรแกรมแบบมัลติเธรดจึงมีการนำแนวคิดพิเศษ mutex มาใช้ (จากภาษาอังกฤษ "mutex", "การยกเว้นร่วมกัน" - "การยกเว้นร่วมกัน" ) วัตถุประสงค์ของ mutexคือการจัดหากลไกเพื่อให้มีเธรดเดียวเท่านั้นที่สามารถเข้าถึงอ็อบเจ็กต์ในช่วงเวลาหนึ่งได้ หาก Thread-1 ได้รับ mutex ของออบเจ็กต์ A แล้ว เธรดอื่นจะไม่สามารถเข้าถึงเพื่อเปลี่ยนแปลงสิ่งใดในนั้นได้ จนกว่า mutex ของอ็อบเจ็กต์ A จะถูกรีลีส เธรดที่เหลือจะถูกบังคับให้รอ ตัวอย่างในชีวิตจริง: ลองจินตนาการว่าคุณและคนแปลกหน้าอีก 10 คนเข้าร่วมการฝึกอบรม คุณต้องผลัดกันแสดงความคิดและอภิปรายบางสิ่งบางอย่าง แต่เนื่องจากคุณพบกันเป็นครั้งแรก เพื่อไม่ให้ขัดจังหวะกันตลอดเวลาและไม่กลายเป็นเสียงขรม คุณจึงใช้กฎ "ลูกบอลพูดได้": มีเพียงคนเดียวเท่านั้นที่สามารถพูดได้ - ผู้ที่มีลูกบอลอยู่ มือของเขา. ด้วยวิธีนี้การอภิปรายจะเพียงพอและเกิดผล โดยพื้นฐานแล้ว mutex ก็คือลูกบอลนั่นเอง หาก mutex ของอ็อบเจ็กต์อยู่ในมือของเธรดเดียว เธรดอื่นจะไม่สามารถเข้าถึงอ็อบเจ็กต์ได้ คุณไม่จำเป็นต้องทำอะไรเพื่อสร้าง 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
       }
   }
}
ความหมายนั้นง่าย หากเธรดหนึ่งเข้าสู่บล็อกของโค้ดที่ถูกทำเครื่องหมายด้วยคำว่า synchronized เธรดนั้นจะได้รับ mutex ของอ็อบเจ็กต์ทันที และเธรดอื่น ๆ ทั้งหมดที่พยายามเข้าสู่บล็อกหรือวิธีการเดียวกันจะถูกบังคับให้รอจนกว่าเธรดก่อนหน้าจะทำงานเสร็จสิ้นและเผยแพร่ เฝ้าสังเกต. การซิงโครไนซ์เธรด  ตัวดำเนินการซิงโครไนซ์ - 3อนึ่ง! ในการบรรยายของหลักสูตร คุณได้เห็นตัวอย่างของการซิงโครไนซ์แล้ว แต่มันดูแตกต่างออกไป:
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
หัวข้อนี้เป็นหัวข้อใหม่สำหรับคุณ และแน่นอนว่าในช่วงแรกๆ จะต้องเกิดความสับสนกับไวยากรณ์ ดังนั้นจำไว้ทันทีเพื่อไม่ให้สับสนกับวิธีการเขียนในภายหลัง วิธีการเขียนทั้งสองวิธีนี้มีความหมายเหมือนกัน:
public void swap() {

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


public synchronized void swap() {

   }
}
ในกรณีแรก คุณจะสร้างบล็อกโค้ดที่ซิงโครไนซ์ทันทีที่เข้าสู่วิธีการ มันถูกซิงโครไนซ์โดยอ็อบเจ็กต์thisนั่นคือ โดยอ็อบเจ็กต์ปัจจุบัน และในตัวอย่างที่สอง คุณใส่คำว่า synchronized ให้กับวิธีการทั้งหมด ไม่จำเป็นต้องระบุออบเจ็กต์ใดๆ ที่มีการซิงโครไนซ์อย่างชัดเจนอีกต่อไป เมื่อวิธีการทั้งหมดถูกทำเครื่องหมายด้วยคำ วิธีการนี้จะถูกซิงโครไนซ์กับออบเจ็กต์ทั้งหมดของคลาสโดยอัตโนมัติ อย่ามาเจาะลึกการอภิปรายว่าวิธีไหนดีกว่ากัน ตอนนี้เลือกสิ่งที่คุณชอบที่สุด :) สิ่งสำคัญคือต้องจำ: คุณสามารถประกาศวิธีการซิงโครไนซ์ได้ก็ต่อเมื่อตรรกะทั้งหมดที่อยู่ภายในนั้นถูกดำเนินการโดยเธรดเดียวในเวลาเดียวกัน ตัวอย่างเช่น ในกรณีนี้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()mutex ของอ็อบเจ็กต์จะไม่ว่าง และThread-2เมื่อคุณพยายามเรียกเมธอดmyClass.swap()เมธอดนั้นจะค้างเพื่อรอให้ mutex เป็นอิสระ หากเรามี 2 เธรดและ 2 ออบเจ็กต์MyClass- myClass1และmyClass2- บนออบเจ็กต์ที่แตกต่างกัน เธรดของเราจะสามารถดำเนินการวิธีการซิงโครไนซ์พร้อมกันได้อย่างง่ายดาย เธรดแรกทำ:
myClass1.swap();
อันที่สองทำ:
myClass2.swap();
ในกรณีนี้ คำสำคัญที่ซิงโครไนซ์ภายในวิธีการswap()จะไม่ส่งผลกระทบต่อการทำงานของโปรแกรม เนื่องจากการซิงโครไนซ์จะดำเนินการกับวัตถุเฉพาะ และกรณีหลังนี้เรามี 2 วัตถุ ดังนั้นเธรดจึงไม่สร้างปัญหาให้กัน ท้ายที่สุดแล้ว วัตถุสองชิ้นมี mutexes ที่แตกต่างกัน 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;
   }

}
ยังไม่ชัดเจนว่าสิ่งใดจะทำหน้าที่เป็น mutex ในกรณีนี้ ท้ายที่สุด เราได้ตัดสินใจแล้วว่าทุกอ็อบเจ็กต์มีมิวเท็กซ์ แต่ปัญหาก็คือการเรียกเมธอดแบบคงที่นั้นMyClass.swap()เราไม่ต้องการอ็อบเจ็กต์: วิธีนี้เป็นแบบสแตติก! แล้วจะเป็นอย่างไรต่อไป? :/ จริงๆ แล้วเรื่องนี้ก็ไม่มีปัญหาอะไร ผู้สร้าง Java ดูแลทุกอย่าง :) หากวิธีการที่มีตรรกะ "มัลติเธรด" ที่สำคัญเป็นแบบคงที่ การซิงโครไนซ์จะดำเนินการตามคลาส เพื่อความชัดเจนยิ่งขึ้น สามารถเขียนโค้ดด้านบนใหม่เป็น:
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