ซิงโครไนซ์และ reentrantlock
ในการเขียนโปรแกรมแบบมัลติเธรดเราใช้ล็อคเมื่อต้องซิงโครไนซ์รหัส Java ให้วิธีการซิงโครไนซ์สองวิธี: ล็อคในตัว (ซิงโครไนซ์) และล็อคที่ชัดเจน (reentrantlock) ล็อคที่ชัดเจนได้รับการแนะนำโดย JDK1.5 อะไรคือความคล้ายคลึงกันและความแตกต่างระหว่างล็อคทั้งสองนี้? เป็นเพียงตัวเลือกหรือมีเหตุผลอื่น? บทความนี้จะค้นหาสำหรับคุณ
// ตัวอย่างของคำหลักที่ซิงโครไนซ์การใช้งานโมฆะแบบซิงโครไนซ์สาธารณะเพิ่ม (int t) {// วิธีการซิงโครไนซ์ this.v += t;} โมฆะแบบคงที่แบบคงที่สาธารณะ -ทั้งหมดนี้เกี่ยวกับการใช้ล็อคในตัวคุณได้เรียนรู้แล้ว
ล็อคในตัวนั้นสะดวกมากในการใช้งานและไม่จำเป็นต้องได้รับและปล่อยอย่างชัดเจน วัตถุใด ๆ สามารถใช้เป็นล็อคในตัว การใช้ล็อคในตัวสามารถแก้สถานการณ์การซิงโครไนซ์ส่วนใหญ่ "วัตถุใด ๆ สามารถใช้เป็นล็อคในตัว" ยังหมายความว่าที่คำหลักที่ซิงโครไนซ์ปรากฏขึ้นมีวัตถุที่เกี่ยวข้อง โดยเฉพาะ:
เมื่อซิงโครไนซ์ทำหน้าที่ตามวิธีปกติวัตถุล็อคคือสิ่งนี้
เมื่อซิงโครไนซ์ทำหน้าที่ในวิธีการคงที่วัตถุล็อคเป็นวัตถุคลาสของคลาสปัจจุบัน
เมื่อซิงโครไนซ์ทำหน้าที่บล็อกรหัสวัตถุล็อคคือ OBJ นี้ในการซิงโครไนซ์ (OBJ)
ล็อคที่ชัดเจน
ล็อคในตัวใช้งานง่ายทำไมคุณต้องล็อคที่ชัดเจนเป็นพิเศษ? เพราะบางสิ่งไม่สามารถทำได้ด้วยล็อคในตัวเช่น:
เราต้องการเพิ่มเวลารอการล็อคและยอมแพ้ก่อนที่จะได้รับการหมดเวลาเพื่อที่เราจะไม่รออย่างไม่สิ้นสุด
เราต้องการที่จะได้รับการล็อคในวิธีที่ขัดจังหวะเพื่อให้เธรดภายนอกส่งสัญญาณขัดจังหวะให้เราและสามารถทำให้เกิดเธรดที่รอการล็อค
เราต้องการรักษาคิวรอหลายครั้งสำหรับล็อคเช่นคิวผู้ผลิตและคิวผู้บริโภคในขณะที่ปรับปรุงประสิทธิภาพของล็อค
ล็อคที่ชัดเจน (reentrantlocks) เกิดขึ้นอย่างเป็นทางการเพื่อแก้ปัญหาความต้องการที่ยืดหยุ่นเหล่านี้ Reentrantlock หมายถึงการล็อคอีกครั้งและอีกครั้งหมายความว่าเธรดสามารถขอล็อคเดียวกันได้หลายครั้งในเวลาเดียวกันโดยไม่ทำให้มันถูกปิดกั้นด้วยตัวเอง นี่คือความแตกต่างระหว่างการล็อคในตัวและล็อคที่ชัดเจน:
Timeable: Renentrantlock.Trylock (Long Timeout, TimeUnit Unit) เป็นวิธีที่จะยุติเวลารอ หากเธรดไม่ได้รับการล็อคภายในเวลาที่กำหนดวิธีการจะส่งคืนเท็จและสิ้นสุดการรอเธรด
ถูกขัดจังหวะ: คุณต้องเห็นการขัดจังหวะการรับรู้ วิธีการหลายเธรดหลายวิธีจะทำให้เกิดข้อยกเว้นนี้ ข้อยกเว้นนี้ไม่ได้เป็นภาระที่เกิดจากข้อบกพร่อง แต่เป็นสิ่งจำเป็นหรือเป็นสิ่งที่ดี การขัดจังหวะทำให้เรามีวิธีที่จะได้รับเธรดให้สิ้นสุดก่อนเวลา (แทนที่จะต้องรอจนกว่าการดำเนินการเธรดจะสิ้นสุดลง) ซึ่งมีประโยชน์มากสำหรับงานที่ต้องใช้เวลานาน สำหรับล็อคในตัวเธรดจะรอล็อคในตัวหากไม่สามารถรับได้ ไม่มีวิธีอื่นที่จะยุติการรอคอยยกเว้นที่จะได้รับการล็อค renentrantlock.lockinctibly () ให้วิธีการที่จะยุติการรอคอยด้วยการขัดจังหวะ
คิวเงื่อนไข: หลังจากเธรดได้รับการล็อคมันอาจเข้าสู่สถานะการรอคอยเนื่องจากกำลังรอเงื่อนไขบางอย่างที่จะเกิดขึ้น (การล็อคในตัวผ่านวัตถุวิธีการรอ () และการล็อคที่ชัดเจนจะผ่านเงื่อนไข Await () วิธีการ) เธรดที่เข้าสู่สถานะการรอคอยจะแขวนและปล่อยล็อคโดยอัตโนมัติและเธรดเหล่านี้จะถูกใส่ลงในคิวเงื่อนไข ซิงโครไนซ์มีคิวแบบมีเงื่อนไขเพียงหนึ่งคิวในขณะที่ reentrantlock สามารถมีคิวเงื่อนไขหลายคิว อะไรคือประโยชน์ของหลายคิว? โปรดอ่านลง
เพรดิเคตแบบมีเงื่อนไข: หลังจากเธรดได้รับการล็อคบางครั้งก็ต้องรอเงื่อนไขที่แน่นอนที่จะพบก่อนที่มันจะสามารถทำสิ่งต่าง ๆ ได้ ตัวอย่างเช่นผู้ผลิตต้องรอจนกว่าแคชจะไม่พอใจก่อนที่จะสามารถใส่ข้อความลงในคิวในขณะที่ผู้บริโภคต้องรอจนกว่าแคชจะไม่ว่างเปล่าก่อนที่จะสามารถดึงข้อความจากคิวได้ เงื่อนไขเหล่านี้เรียกว่าเพรดิเคตแบบมีเงื่อนไข เธรดจำเป็นต้องได้รับการล็อคก่อนจากนั้นพิจารณาว่าเพรดิเคตแบบมีเงื่อนไขเป็นที่พอใจหรือไม่ หากไม่พอใจก็จะไม่ถูกดำเนินการลง เธรดที่เกี่ยวข้องจะให้การดำเนินการถูกต้องและปล่อยล็อคโดยอัตโนมัติ เธรดที่แตกต่างกันโดยใช้ล็อคเดียวกันอาจมีภาคการศึกษาที่มีเงื่อนไขแตกต่างกัน หากมีเพียงคิวแบบมีเงื่อนไขเพียงหนึ่งคิวเมื่อมีความพึงพอใจตามเงื่อนไขจะเป็นไปไม่ได้ที่จะกำหนดว่าเธรดใดในคิวแบบมีเงื่อนไขที่จะตื่นขึ้นมา; แต่ถ้าแต่ละภาควิชามีเงื่อนไขมีคิวแบบมีเงื่อนไขแยกต่างหากเมื่อมีความพึงพอใจตามเงื่อนไขเรารู้ว่าเธรดบนคิวที่สอดคล้องกันควรถูกปลุก (ล็อคในตัวถูกปลุกให้ตื่นขึ้นผ่านวัตถุ Notify () หรือ Object.Notifyall () วิธีการที่ชัดเจนผ่านเงื่อนไข นี่คือประโยชน์ของคิวเงื่อนไขหลายข้อ
เมื่อใช้ล็อคในตัววัตถุเองทั้งล็อคและคิวแบบมีเงื่อนไข เมื่อใช้ล็อคที่ชัดเจนวัตถุของ Renentrantlock คือการล็อคและคิวแบบมีเงื่อนไขจะได้รับผ่านวิธีการ renentrantlock.newCondition () สามารถรับคิวแบบมีเงื่อนไขหลายคิวได้โดยเรียกวิธีนี้หลายครั้ง
ตัวอย่างทั่วไปของการใช้ล็อคที่ชัดเจนมีดังนี้:
// ตัวอย่างของการใช้การล็อคอย่างชัดเจน reentrantlock lock = ใหม่ reentrantlock (); // ซื้อล็อคนี่คือการใช้งานที่สอดคล้องกับคำหลักที่ซิงโครไนซ์ lock.lock (); ลอง {// รหัสของคุณ} ในที่สุด {lock.unlock ();} // คุณสามารถใช้เวลาและเลิกล็อคหากคุณได้รับการล็อคหลังจากเวลาที่ระบุเกิน ลอง {Lock.Trylock (10, TimeUnit.Seconds); ลอง {// รหัสของคุณ} ในที่สุด {lock.unlock (); }} catch (interruptedException e1) {// การจัดการข้อยกเว้น} // คุณสามารถขัดจังหวะและเธรดเธรดสามารถถูกขัดจังหวะได้ในขณะที่รอการล็อคที่จะได้รับลอง {lock.lockinctibly (); ลอง {// รหัสของคุณ} ในที่สุด {lock.unlock (); }} catch (interruptedException e) {// การจัดการข้อยกเว้น} // คิวรอหลายรายการโปรดดู [arrayblockingqueue] (https://github.com/carpenterlee/jcrecopes/blob/master/markdown/arrayblockingqueue.md) วาง */เงื่อนไขสุดท้ายส่วนตัว notfull = lock.newCondition ();โปรดทราบว่ารหัสด้านบนวางปลดล็อค () ในบล็อกในที่สุดซึ่งจำเป็น ล็อคที่ชัดเจนจะไม่ถูกปล่อยออกมาโดยอัตโนมัติเช่นล็อคในตัว เมื่อใช้การล็อคที่ชัดเจนจะต้องเปิดตัวด้วยตนเองในบล็อกในที่สุด หากการล็อคไม่ได้รับการปล่อยตัวเนื่องจากข้อยกเว้นหลังจากได้รับล็อคแล้วล็อคจะไม่ถูกปล่อยออกมา! วางปลดล็อค () ในที่สุดบล็อกเพื่อให้แน่ใจว่าสามารถปล่อยได้ตามปกติไม่ว่าจะเกิดอะไรขึ้น
สรุปแล้ว
ล็อคในตัวสามารถแก้สถานการณ์ส่วนใหญ่ที่ต้องการการซิงโครไนซ์ เฉพาะเมื่อจำเป็นต้องมีความยืดหยุ่นเพิ่มเติมจะต้องพิจารณาล็อคที่ชัดเจนเช่นเวลาการขัดจังหวะและรอคิวหลายครั้ง
แม้ว่าการล็อคที่ชัดเจนจะยืดหยุ่น แต่พวกเขาต้องการแอปพลิเคชันและการเปิดตัวที่ชัดเจนและจะต้องวางจำหน่ายในบล็อกในที่สุดมิฉะนั้นการล็อคอาจไม่ถูกปล่อยออกมาเนื่องจากข้อยกเว้น! นี่เป็นข้อเสียที่ชัดเจนที่สุดของการล็อคที่ชัดเจน
โดยสรุปเมื่อซิงโครไนซ์โปรดให้ความสำคัญกับความปลอดภัยและง่ายกว่าที่จะใช้ล็อคโดยนัย