3.1 วัตถุที่ใช้งานอยู่
วัตถุที่ใช้งานอยู่คือรูปแบบการออกแบบที่แยกเธรดการดำเนินการของเมธอดออกจากเธรดที่ถูกเรียกใช้ จุดประสงค์ของรูปแบบนี้คือเพื่อให้การดำเนินการแบบขนานโดยใช้การเรียกใช้เมธอดแบบอะซิงโครนัสและตัวจัดกำหนดการการประมวลผลคำขอ
รุ่นประยุกต์:
![วัตถุที่ใช้งานอยู่](https://cdn.codegym.cc/images/article/19841270-318f-4d3e-ad78-23c7e8acf225/800.jpeg)
รุ่นคลาสสิก:
![วัตถุที่ใช้งาน 2](https://cdn.codegym.cc/images/article/db9858eb-5021-40ae-a561-9989b344f1fd/800.jpeg)
เทมเพลตนี้มีหกองค์ประกอบ:
- วัตถุพร็อกซีที่ให้อินเทอร์เฟซกับวิธีการสาธารณะของไคลเอ็นต์
- อินเทอร์เฟซที่กำหนดวิธีการเข้าถึงสำหรับวัตถุที่ใช้งานอยู่
- รายการคำขอรับจากลูกค้า
- ตัวกำหนดตารางเวลาที่กำหนดลำดับในการสืบค้นที่จะดำเนินการ
- การใช้วิธีวัตถุที่ใช้งานอยู่
- ขั้นตอนการโทรกลับหรือตัวแปรสำหรับไคลเอ็นต์เพื่อรับผลลัพธ์
3.2 ล็อค
รูปแบบการล็อคเป็นกลไกการซิงโครไนซ์ที่ช่วยให้สามารถเข้าถึงทรัพยากรที่ใช้ร่วมกันระหว่างหลายเธรด การล็อกเป็นวิธีหนึ่งในการบังคับใช้นโยบายควบคุมการทำงานพร้อมกัน
![](https://cdn.codegym.cc/images/article/c1de466a-7e83-4372-a79e-7344cbe094f8/800.jpeg)
โดยพื้นฐานแล้ว ซอฟต์ล็อคจะถูกใช้โดยมีข้อสันนิษฐานว่าแต่ละเธรดพยายามที่จะ "รับการล็อก" ก่อนที่จะเข้าถึงทรัพยากรที่ใช้ร่วมกันที่สอดคล้องกัน
อย่างไรก็ตาม บางระบบมีกลไกการล็อกที่จำเป็น โดยความพยายามเข้าถึงทรัพยากรที่ถูกล็อกโดยไม่ได้รับอนุญาตจะถูกยกเลิกโดยการโยนข้อยกเว้นบนเธรดที่พยายามเข้าถึง
สัญญาณเป็นประเภทล็อคที่ง่ายที่สุด ในแง่ของการเข้าถึงข้อมูล ไม่มีความแตกต่างระหว่างโหมดการเข้าถึง: แชร์ (อ่านอย่างเดียว) หรือพิเศษ (อ่าน-เขียน) ในโหมดที่ใช้ร่วมกัน เธรดจำนวนมากสามารถร้องขอการล็อกเพื่อเข้าถึงข้อมูลในโหมดอ่านอย่างเดียว โหมดการเข้าถึงพิเศษยังใช้ในการปรับปรุงและลบอัลกอริทึม
![รูปแบบการล็อค](https://cdn.codegym.cc/images/article/14da5562-e2ce-49f4-b330-3312f977f4b4/800.jpeg)
ประเภทของการล็อคนั้นแตกต่างกันไปตามกลยุทธ์ในการบล็อกความต่อเนื่องของการดำเนินการของเธรด ในการใช้งานส่วนใหญ่ การร้องขอการล็อกจะป้องกันไม่ให้เธรดดำเนินการต่อไปจนกว่าทรัพยากรที่ถูกล็อกจะพร้อมใช้งาน
spinlockคือการล็อคที่รอเป็นวงรอบจนกว่าจะได้รับสิทธิ์การเข้าถึง การล็อกดังกล่าวจะมีประสิทธิภาพมากหากเธรดรอการล็อกเป็นเวลาเล็กน้อย จึงหลีกเลี่ยงการเปลี่ยนตารางเธรดมากเกินไป ค่าใช้จ่ายในการรอการเข้าถึงจะมีนัยสำคัญหากเธรดตัวใดตัวหนึ่งล็อกไว้เป็นเวลานาน
![รูปแบบการล็อค 2](https://cdn.codegym.cc/images/article/8c7fe29c-5d7b-4888-9745-b45fbe9a0424/800.jpeg)
หากต้องการใช้กลไกการล็อคอย่างมีประสิทธิภาพ จำเป็นต้องมีการสนับสนุนในระดับฮาร์ดแวร์ การสนับสนุนฮาร์ดแวร์สามารถดำเนินการเป็นหนึ่งหรือหลายการดำเนินการเช่น "ทดสอบและตั้งค่า", "ดึงและเพิ่ม" หรือ "เปรียบเทียบและสลับ" คำแนะนำดังกล่าวช่วยให้คุณตรวจสอบได้โดยไม่หยุดชะงักว่าล็อคว่างหรือไม่ และถ้าใช่ ให้ทำการล็อค
3.3 จอภาพ
รูปแบบการตรวจสอบคือการโต้ตอบกระบวนการระดับสูงและกลไกการซิงโครไนซ์ที่ให้การเข้าถึงทรัพยากรที่ใช้ร่วมกัน วิธีการซิงโครไนซ์งานคอมพิวเตอร์ตั้งแต่สองงานขึ้นไปโดยใช้ทรัพยากรร่วมกัน โดยปกติจะเป็นฮาร์ดแวร์หรือชุดของตัวแปร
ในการทำงานหลายอย่างพร้อมกันบนจอภาพ คอมไพเลอร์หรือล่ามจะแทรกรหัสล็อคและปลดล็อคอย่างโปร่งใสลงในรูทีนที่มีรูปแบบเหมาะสม ให้กับโปรแกรมเมอร์อย่างโปร่งใส ประหยัดโปรแกรมเมอร์จากการเรียกดั้งเดิมของการซิงโครไนซ์อย่างชัดเจน
![](https://cdn.codegym.cc/images/article/3b7a2de5-610e-43da-9752-9ae8fa604eb8/800.jpeg)
จอภาพประกอบด้วย:
- ชุดของขั้นตอนที่โต้ตอบกับทรัพยากรที่ใช้ร่วมกัน
- มิวเท็กซ์
- ตัวแปรที่เกี่ยวข้องกับทรัพยากรนี้
- ค่าคงที่ที่กำหนดเงื่อนไขเพื่อหลีกเลี่ยงสภาวะการแข่งขัน
ขั้นตอนการตรวจสอบจะรับ mutex ก่อนเริ่มทำงานและคงไว้จนกว่าขั้นตอนจะออกหรือจนกว่าจะมีเงื่อนไขบางอย่างรออยู่ หากแต่ละขั้นตอนรับประกันว่าค่าคงที่นั้นเป็นจริงก่อนที่จะปล่อย mutex จะไม่มีงานใดสามารถรับทรัพยากรในสภาวะการแย่งชิงได้
นี่คือวิธีที่ ตัวดำเนินการ ซิงโครไนซ์ ทำงานใน Java ด้วยwait()
and วิธี notify()
การ
3.4 ตรวจสอบการล็อกสองครั้ง
การล็อคแบบตรวจสอบซ้ำสองครั้งเป็นรูปแบบการออกแบบแบบคู่ขนานที่มีจุดประสงค์เพื่อลดค่าใช้จ่ายในการรับล็อค
ประการแรก เงื่อนไขการบล็อกจะถูกตรวจสอบโดยไม่มีการซิงโครไนซ์ใดๆ เธรดพยายามที่จะรับล็อคก็ต่อเมื่อผลการตรวจสอบระบุว่าจำเป็นต้องได้รับการล็อค
//Double-Checked Locking
public final class Singleton {
private static Singleton instance; //Don't forget volatile modifier
public static Singleton getInstance() {
if (instance == null) { //Read
synchronized (Singleton.class) { //
if (instance == null) { //Read Write
instance = new Singleton(); //
}
}
}
}
จะสร้าง singleton object ในสภาวะแวดล้อมแบบ thread-safe ได้อย่างไร?
public static Singleton getInstance() {
if (instance == null)
instance = new Singleton();
}
หากคุณสร้างออบเจกต์ Singleton จากเธรดที่แตกต่างกัน อาจมีสถานการณ์ที่ออบเจ็กต์หลายรายการถูกสร้างขึ้นพร้อมกัน ซึ่งเป็นสิ่งที่ยอมรับไม่ได้ ดังนั้นจึงมีเหตุผลที่จะรวมการสร้างวัตถุไว้ในคำสั่งที่ซิงโครไนซ์
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
วิธีการนี้จะได้ผล แต่ก็มีข้อเสียเล็กน้อย หลังจากสร้างวัตถุแล้ว ทุกครั้งที่คุณพยายามรับวัตถุในอนาคต การตรวจสอบจะดำเนินการในบล็อกที่ซิงโครไนซ์ ซึ่งหมายความว่าเธรดปัจจุบันและทุกสิ่งที่เชื่อมต่อด้วยจะถูกล็อก ดังนั้นรหัสนี้สามารถปรับปรุงได้เล็กน้อย:
public static Singleton getInstance() {
if (instance != null)
return instance;
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
ในบางภาษาและ/หรือในบางเครื่อง ไม่สามารถใช้รูปแบบนี้ได้อย่างปลอดภัย ดังนั้นบางครั้งจึงเรียกว่ารูปแบบต่อต้าน คุณลักษณะดังกล่าวนำไปสู่การปรากฏของความสัมพันธ์ลำดับที่เข้มงวด "เกิดขึ้นก่อน" ใน Java Memory Model และ C++ Memory Model
โดยทั่วไปจะใช้เพื่อลดโอเวอร์เฮดของการนำการกำหนดค่าเริ่มต้นแบบสันหลังยาวไปใช้ในโปรแกรมแบบมัลติเธรด เช่น รูปแบบการออกแบบ Singleton ในการกำหนดค่าเริ่มต้นของตัวแปรแบบขี้เกียจ การกำหนดค่าเริ่มต้นจะถูกเลื่อนออกไปจนกว่าจะจำเป็นต้องใช้ค่าของตัวแปรในการคำนวณ
3.5 ตัวกำหนดตารางเวลา
ตัวจัดกำหนดการเป็นรูปแบบการออกแบบแบบขนานที่ให้กลไกสำหรับการนำนโยบายการจัดกำหนดการไปใช้ แต่ไม่ขึ้นกับนโยบายเฉพาะใดๆ ควบคุมลำดับที่เธรดควรรันโค้ดตามลำดับ โดยใช้อ็อบเจ็กต์ที่ระบุลำดับของเธรดที่รออย่างชัดเจน
![](https://cdn.codegym.cc/images/article/6cf63a65-1fe5-44f8-a43c-6f5219728c9a/800.jpeg)