บทความนี้ส่วนใหญ่ศึกษารหัสตัวอย่างแอปพลิเคชันของเงื่อนไขการปิดกั้นเงื่อนไขการปิดกั้น Java ดังต่อไปนี้
เงื่อนไขจะแบ่งวิธีการตรวจสอบวัตถุ (รอแจ้งและแจ้งเตือน) ลงในวัตถุที่แตกต่างกันอย่างสิ้นเชิงเพื่อให้การรวมวัตถุเหล่านี้เข้ากับการใช้งานล็อคใด ๆ แต่ละวัตถุจะมีชุดรอหลายชุด ในหมู่พวกเขาล็อคแทนที่การใช้วิธีการที่ซิงโครไนซ์และคำสั่งและเงื่อนไขแทนที่การใช้วิธีการตรวจสอบวัตถุ
เนื่องจากเงื่อนไขสามารถใช้แทนการรอการแจ้งเตือนและวิธีการอื่น ๆ เราจึงสามารถเปรียบเทียบรหัสของการสื่อสารระหว่างเธรดที่เขียนก่อนและดูปัญหาดั้งเดิม:
มีสองเธรด เธรดเด็กจะดำเนินการ 10 ครั้งก่อนจากนั้นเธรดหลักจะดำเนินการ 5 ครั้งจากนั้นสลับไปที่เธรดลูกจะดำเนินการ 10 จากนั้นเธรดหลักจะดำเนินการ 5 ครั้ง ... การเดินทางไปรอบนี้คือ 50 ครั้ง
ฉันใช้รอและแจ้งให้นำไปใช้ก่อนหน้านี้ แต่ตอนนี้ฉันใช้เงื่อนไขในการเขียนใหม่รหัสมีดังนี้:
การสื่อสารระดับสาธารณะระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ธุรกิจธุรกิจ = ธุรกิจใหม่ (); เธรดใหม่ (ใหม่ runnable () {// เปิดเธรดเด็ก @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 1; i <= 50; i ++) 50; i ++) {bussiness.main (i);}}}} คลาสธุรกิจ {ล็อคล็อค = ใหม่ reentrantlock () เงื่อนไขสภาพ = ล็อคนิวคิว {condition.await (); // ใช้เงื่อนไขเพื่อเรียกวิธีการรอคอย} catch (Exception e) {// toDo catch blocke.printstacktrace () {// toDo สำหรับ (int j = 1; j <= 10; j ++) FALSE; edition.signal (); // ใช้เงื่อนไขเพื่อส่งสัญญาณปลุกและตื่นขึ้นมาหนึ่ง} ในที่สุด {lock.unlock ();}} โมฆะสาธารณะหลัก (int i) {lock.lock (); ลอง {ในขณะที่ (bshouldsub) {ลอง {เงื่อนไข blocke.printstacktrace ();}} สำหรับ (int j = 1; j <= 10; j ++) {system.out.println ("ลำดับด้ายหลักของ" + j + ", ลูปของ" + i);} bshouldsub = true;จากมุมมองของรหัสจะใช้เงื่อนไขร่วมกับล็อค หากไม่มีล็อคไม่สามารถใช้เงื่อนไขได้เนื่องจากสภาพถูกสร้างขึ้นผ่านการล็อค การใช้งานนี้ง่ายมาก ตราบใดที่คุณเชี่ยวชาญการใช้การซิงโครไนซ์รอและแจ้งให้ทราบคุณสามารถใช้การใช้ล็อคและเงื่อนไขได้อย่างเต็มที่
ข้างต้นใช้การล็อคและเงื่อนไขแทนวิธีการซิงโครไนซ์และการตรวจสอบวัตถุเพื่อตระหนักถึงการสื่อสารระหว่างสองเธรด ทีนี้มาเขียนแอปพลิเคชันขั้นสูงขึ้นเล็กน้อย: จำลองคิวการบล็อกของบัฟเฟอร์
บัฟเฟอร์คืออะไร? ตัวอย่างเช่นมีหลายคนที่ต้องการส่งข้อความตอนนี้ ฉันเป็นสถานีขนส่งและฉันต้องการช่วยเหลือผู้อื่นส่งข้อความ ตอนนี้ฉันต้องทำสองสิ่ง สิ่งหนึ่งคือการรับข้อความที่ผู้ใช้ส่งและใส่ไว้ในบัฟเฟอร์ตามลำดับ อีกสิ่งหนึ่งคือการนำข้อความที่ผู้ใช้ส่งออกไปตามลำดับจากบัฟเฟอร์และส่งออกไป
ตอนนี้บทคัดย่อปัญหาจริงนี้: บัฟเฟอร์เป็นอาร์เรย์ เราสามารถเขียนข้อมูลลงในอาร์เรย์หรือนำข้อมูลออกจากอาร์เรย์ สองสิ่งที่ฉันต้องทำคือการเริ่มต้นสองเธรดหนึ่งเพื่อจัดเก็บข้อมูลและอื่น ๆ เพื่อรับข้อมูล แต่ปัญหาคือถ้าบัฟเฟอร์เต็มหมายความว่ามีข้อความที่ได้รับมากเกินไปนั่นคือข้อความที่ส่งเร็วเกินไปและเธรดอื่นของชีวิตของฉันไม่เพียงพอที่จะส่งมันส่งผลให้บัฟเฟอร์ถูกทิ้งไว้ดังนั้นเธรดของการจัดเก็บข้อมูลจะต้องถูกบล็อกและปล่อยให้มันรอ; ในทางตรงกันข้ามถ้าฉันส่งต่อมันเร็วเกินไปและตอนนี้เนื้อหาทั้งหมดของบัฟเฟอร์ถูกส่งโดยฉันและไม่มีการส่งข้อความใหม่โดยผู้ใช้แล้วเธรดของการเก็บข้อมูลจะต้องถูกบล็อกในเวลานี้
ตกลงหลังจากวิเคราะห์คิวการปิดกั้นของบัฟเฟอร์นี้ให้ใช้เทคโนโลยีเงื่อนไขเพื่อนำไปใช้:
บัฟเฟอร์คลาส {ล็อคสุดท้ายล็อค = ใหม่ reentrantlock (); // กำหนดเงื่อนไขสุดท้ายล็อค notfull = lock.newcondition (); // กำหนดเงื่อนไขเงื่อนไข notempty = lock.newCondition (); // define stumfinal วัตถุ [] count; // array subscripts ใช้เพื่อปรับเทียบตำแหน่ง // ข้อมูลการจัดเก็บลงในช่องว่างสาธารณะของคิวใส่ (วัตถุ x) พ่น InterruptedException {lock.lock (); // ล็อคลอง {ในขณะที่ (count == items.length) {system.out.println (thread.currentthread การเป็น! "); notfull.await (); // ถ้าคิวเต็มแล้วปิดกั้นเธรดของการจัดเก็บข้อมูลรอที่จะตื่นขึ้นมา} // ถ้ามันไม่เต็มให้เก็บของเก็บ [putptr] = x; ถ้า (++ putptr == รายการความยาว) // นี่คือคำพิพากษา หากถึงแล้วให้ย้อนกลับไปที่จุดเริ่มต้น putptr = 0; ++ count; // จำนวนของข้อความข้อความ out.println (thread.currentthread (). getName () + "บันทึกค่า:" + x); notempty.signal (); // โอเคตอนนี้มีข้อมูลในคิว ปลุกเธรดด้วยคิวเปล่าและคุณจะได้รับข้อมูล} ในที่สุด {lock.unlock (); // ดาวน์โหลดล็อค}} // ดึงข้อมูลจากวัตถุสาธารณะคิวใช้ () พ่น InterruptedException {lock.lock (); เวลาเป็น! "); notempty.await (); // ถ้าคิวว่างเปล่าแล้วเธรดจะบล็อกข้อมูลเพื่อดึงเธรดรอที่จะตื่นขึ้นมา} // ถ้ามันไม่ว่าง System.out.println (thread.currentthread (). getName () + "ออกค่า:" + x); notfull.signal (); // โอเคตอนนี้มีที่ตั้งในคิว ปลุกเธรดที่เต็มไปด้วยคิวและคุณสามารถจัดเก็บข้อมูล return x;} ในที่สุด {lock.unlock (); // lock}}}โปรแกรมนี้คลาสสิกฉันนำมันออกจากเอกสาร JDK อย่างเป็นทางการและเพิ่มความคิดเห็น มีสองเงื่อนไขที่กำหนดไว้ในโปรแกรมซึ่งใช้เพื่อดำเนินการสองเธรดและรอและตื่นขึ้นมาตามลำดับ ความคิดนั้นชัดเจนและโปรแกรมก็แข็งแกร่งมาก สามารถพิจารณาคำถามหนึ่งคำถามทำไมคุณต้องใช้สองรหัส? จะต้องมีเหตุผลสำหรับการออกแบบนี้ หากคุณใช้เงื่อนไขตอนนี้สมมติว่าคิวเต็ม แต่มี 2 เธรด A และ B ที่เก็บข้อมูลในเวลาเดียวกันพวกเขาทั้งหมดเข้าสู่การนอนหลับ ตกลงตอนนี้อีกเธรดจะหายไปหนึ่งแล้วก็ตื่นขึ้นมาหนึ่งในเธรด A จากนั้น A สามารถบันทึกได้ หลังจากบันทึกแล้วตื่นขึ้นมาอีกเธรด หาก B ถูกปลุกให้ตื่นขึ้นจะมีปัญหาเพราะคิวเต็มในเวลานี้และ B ไม่สามารถเก็บไว้ได้ หากร้านค้า B มันจะเขียนทับค่าที่ไม่ได้รับการเรียกคืน เนื่องจากมีการใช้เงื่อนไขทั้งการจัดเก็บและการถอนใช้เงื่อนไขนี้ในการนอนหลับและตื่นขึ้นมาและมันจะยุ่งเหยิง ณ จุดนี้คุณสามารถเข้าใจการใช้เงื่อนไขนี้ ตอนนี้ลองทดสอบผลกระทบของคิวการบล็อกด้านบน:
คลาสสาธารณะ BoundedBuffer {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {บัฟเฟอร์บัฟเฟอร์ = บัฟเฟอร์ใหม่ (); สำหรับ (int i = 0; i <5; i ++) {// เปิด 5 เธรดเพื่อเก็บข้อมูลในบัฟเฟอร์ใหม่ (interruptedException e) {E.printStackTrace ();}}}). start ();} สำหรับ (int i = 0; i <10; i ++) {// เปิด 10 เธรดเพื่อรับข้อมูลจากเธรดใหม่ {E.PrintStackTrace ();}}}). start ();}}}}ฉันเปิดใช้งานเธรดเพียง 5 เธรดเพื่อจัดเก็บข้อมูลและ 10 เธรดเพื่อดึงข้อมูลเพียงเพื่อให้มันถูกบล็อกจากการรับข้อมูลและดูผลลัพธ์ของการดำเนินการ:
Thread-5 ถูกบล็อกและไม่สามารถเรียกคืนข้อมูลได้ในขณะนี้!
เธรด -10 ถูกบล็อกและไม่สามารถเรียกคืนข้อมูลได้ในขณะนี้!
Thread-1 Saved Value: 755
เธรด -0 ค่าที่บันทึกไว้: 206
Thread-2 Saved Value: 741
เธรด -3 ค่าที่บันทึกไว้: 381
เธรด -14 ลบค่า: 755
Thread-4 ค่าที่บันทึกไว้: 783
Thread-6 นำค่าออกมา: 206
เธรด -7 ลบค่า: 741
เธรด -8 ลบค่า: 381
Thread-9 นำค่าออกมา: 783
Thread-5 ถูกบล็อกและไม่สามารถเรียกคืนข้อมูลได้ในขณะนี้!
เธรด -11 ถูกบล็อกและไม่สามารถเรียกคืนข้อมูลได้ในขณะนี้!
เธรด -12 ถูกบล็อกและไม่สามารถเรียกคืนข้อมูลได้ในขณะนี้!
เธรด -10 ถูกบล็อกและไม่สามารถเรียกคืนข้อมูลได้ในขณะนี้!
เธรด -13 ถูกบล็อกและไม่สามารถเรียกคืนข้อมูลได้ในขณะนี้!
จากผลลัพธ์เราจะเห็นว่าเธรด 5 และ 10 ถูกดำเนินการก่อนและพวกเขาพบว่าไม่มีในคิวดังนั้นพวกเขาจึงถูกบล็อกและนอนหลับที่นั่น พวกเขาสามารถรับได้จนกว่าค่าใหม่จะถูกเก็บไว้ในคิว อย่างไรก็ตามพวกเขาไม่ได้โชคดีและข้อมูลที่เก็บไว้จะถูกนำมาใช้เป็นอันดับแรกโดยเธรดอื่น ๆ ฮ่าฮ่า ... พวกเขาสามารถวิ่งได้อีกสองสามครั้ง หากคุณต้องการดูข้อมูลที่เก็บไว้ถูกบล็อกคุณสามารถตั้งค่าเธรดเพื่อดึงข้อมูลน้อยลงเล็กน้อยและฉันจะไม่ตั้งค่าที่นี่
มันยังคงเป็นคำถามเดียวกันกับก่อน ตอนนี้ให้สามเธรดดำเนินการ มาดูคำถาม:
มีสามเธรด, เธรดลูก 1 ดำเนินการ 10 ครั้งก่อน, เธรดลูก 2 ดำเนินการ 10 ครั้งจากนั้นเธรดหลักจะดำเนินการ 5 ครั้งจากนั้นสลับไปที่เธรดลูก 1 ดำเนินการ 10 ครั้ง, เธรดลูก 2 ดำเนินการ 10 ครั้ง, เธรดหลักดำเนินการ 5 ครั้ง ... การเดินทางรอบนี้คือ 50 ครั้ง
หากคุณไม่ได้ใช้เงื่อนไขมันยากมากที่จะทำ แต่มันสะดวกมากที่จะทำตามเงื่อนไข หลักการนั้นง่ายมาก กำหนดสามเงื่อนไข หลังจากที่เธรดเด็ก 1 ดำเนินการเธรดเด็ก 2 ตื่นขึ้นมาด้ายหลักและเธรดหลักจะปลุกเธรดลูก 1 กลไกการปลุกจะคล้ายกับบัฟเฟอร์ด้านบน มาดูรหัสด้านล่างมันง่ายที่จะเข้าใจ
ระดับสาธารณะ threeconditionCommunication {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ธุรกิจธุรกิจ = ธุรกิจใหม่ (); เธรดใหม่ (ใหม่ runnable () {// เปิดเธรดเด็ก @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 1; i <= 50; i ++) {bussiness.sub1 (i); {// เริ่มต้นด้ายลูกอื่น @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 1; i <= 50; i ++) {bussiness.sub2 (i);}}}) เริ่มต้น (); // วิธีการหลักหลักสำหรับ (int i = 1; i <= 50; i ++) {bussiness reentrantlock (); เงื่อนไขเงื่อนไข 1 = lock.newCondition (); // เงื่อนไขเป็นเงื่อนไข 2 = lock.newCondition (); เงื่อนไขเงื่อนไข = lock.newCondition (); int ส่วนตัว bshouldSub = 0; void sub1 (int i) {lock.lock () วิธีการรอ} catch (Exception e) {// toDo catch ที่สร้างขึ้นอัตโนมัติ catch.printstacktrace ();}} สำหรับ (int j = 1; j <= 10; j ++) {system.out.println ("sub1 ลำดับของ" + j + " {lock.unlock ();}} โมฆะสาธารณะ sub2 (int i) {lock.lock (); ลอง {ในขณะที่ (bshouldsub! = 1) {ลอง {storam2.await (); // ใช้เงื่อนไขเพื่อเรียกวิธีการรอคอย j ++) {system.out.println ("Sub2 Thread Sequence ของ" + j + ", loop ของ" + i);} bshouldsub = 2; conditionmain.signal (); // ให้เธรดหลักดำเนินการ} ในที่สุด {lock.unlock ()}}} สาธารณะ {conditionmain.await (); // ใช้เงื่อนไขเพื่อเรียกวิธีการรอคอย} catch (Exception e) {// toDo catch ที่สร้างอัตโนมัติที่สร้างขึ้นอัตโนมัติ 0; เงื่อนไข 1.signal (); // ปล่อยเธรด 1 Execute} ในที่สุด {lock.unlock ();}}}}}รหัสดูเหมือนว่าค่อนข้างยาว แต่มันเป็นภาพลวงตาและตรรกะนั้นง่ายมาก นั่นคือทั้งหมดสำหรับการสรุปเทคโนโลยีเงื่อนไขในเธรด
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้เกี่ยวกับตัวอย่างรหัสแอปพลิเคชันของเงื่อนไขการปิดกั้นเงื่อนไขการทำงานพร้อมกันของ Java ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน เพื่อนที่สนใจสามารถอ้างถึงหัวข้ออื่น ๆ ที่เกี่ยวข้องในเว็บไซต์นี้ต่อไป หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น ขอบคุณเพื่อนที่ให้การสนับสนุนเว็บไซต์นี้!