คำนำ
สำหรับ InterruptedException วิธีทั่วไปในการจัดการมันคือ "กลืน" มัน - จับมันและไม่ทำอะไรเลย (หรือบันทึก แต่ก็ไม่ได้ดีกว่ามาก) - เช่นเดียวกับรายการ 4 ในภายหลัง น่าเสียดายที่วิธีการนี้ไม่สนใจความจริงที่ว่าการขัดจังหวะอาจเกิดขึ้นในช่วงเวลานี้และการขัดจังหวะอาจทำให้แอปพลิเคชันสูญเสียความสามารถในการยกเลิกกิจกรรมหรือปิดในเวลา
วิธีการปิดกั้น
เมื่อวิธีการพ่น InterruptedException มันไม่เพียง แต่บอกคุณว่ามันสามารถโยนข้อยกเว้นการตรวจสอบเฉพาะ แต่มันยังบอกคุณอย่างอื่น ตัวอย่างเช่นมันบอกคุณว่ามันเป็นวิธีการบล็อกซึ่งจะพยายามกำจัดการบล็อกและกลับมาเร็วที่สุดเท่าที่จะเป็นไปได้หากคุณตอบสนองอย่างถูกต้อง
วิธีการบล็อกนั้นแตกต่างจากวิธีการทั่วไปที่ใช้เวลานานในการทำงาน ความสมบูรณ์ของวิธีการทั่วไปขึ้นอยู่กับสิ่งที่กำลังทำและมีทรัพยากรการคำนวณเพียงพอหรือไม่ (CPU รอบและหน่วยความจำ) ความสมบูรณ์ของวิธีการบล็อกยังขึ้นอยู่กับเหตุการณ์ภายนอกบางอย่างเช่นการหมดอายุการจับเวลาการเสร็จสิ้น I/O หรือการกระทำของเธรดอื่น (ปล่อยล็อคตั้งค่าสถานะหรือวางงานในคิวงาน) วิธีการทั่วไปสิ้นสุดลงหลังจากการทำงานเสร็จแล้วในขณะที่วิธีการปิดกั้นนั้นยากที่จะทำนายเพราะพวกเขาขึ้นอยู่กับเหตุการณ์ภายนอก วิธีการปิดกั้นอาจส่งผลกระทบต่อการตอบสนองเพราะเป็นการยากที่จะทำนายเมื่อพวกเขาจะสิ้นสุดเมื่อใด
วิธีการปิดกั้นอาจไม่ถูกยกเลิกเนื่องจากไม่สามารถรอเหตุการณ์ที่รอคอยได้ดังนั้นจึงมีประโยชน์มากในการทำให้วิธีการปิดกั้นสามารถยกเลิกได้ (และมักจะมีประโยชน์มากหากวิธีการไม่ปิดกั้นระยะยาวนั้นสามารถยกเลิกได้) การดำเนินการที่ยกเลิกได้หมายถึงการดำเนินการที่สามารถยกเลิกได้จากภายนอกก่อนที่จะเสร็จสิ้นปกติ กลไกการขัดจังหวะที่จัดทำโดยเธรดและสนับสนุนโดย thread.sleep () และ Object.wait () เป็นกลไกการยกเลิก อนุญาตให้หนึ่งเธรดหนึ่งขอให้เธรดอื่นหยุดสิ่งที่กำลังทำอยู่ เมื่อวิธีการพุ่งเข้าหาการขัดจังหวะการรับรู้มันจะบอกคุณว่าหากเธรดดำเนินการวิธีการถูกขัดจังหวะมันจะพยายามหยุดสิ่งที่มันกำลังทำและกลับมาล่วงหน้าและโดยการขว้าง InterruptedException วิธีการปิดกั้นห้องสมุดที่ประพฤติตัวดีควรตอบสนองต่อการขัดจังหวะและโยน InterruptedException เพื่อให้สามารถใช้ในกิจกรรมที่ยกเลิกได้โดยไม่ส่งผลกระทบต่อการตอบสนอง
เธรดขัดจังหวะ
แต่ละเธรดมีแอตทริบิวต์บูลีนที่เกี่ยวข้องซึ่งแสดงถึงสถานะการขัดจังหวะของเธรด สถานะการขัดจังหวะเป็นเท็จในตอนแรก เมื่อเธรดอื่นขัดจังหวะเธรดโดยเรียกเธรด interrupt () หนึ่งในสองสถานการณ์จะเกิดขึ้น หากเธรดนั้นกำลังดำเนินการวิธีการปิดกั้นที่ไม่สามารถขัดจังหวะระดับต่ำเช่น thread.sleep (), thread.join () หรือ Object.wait () มันจะปลดบล็อกและโยน InterruptedException มิฉะนั้น interrupt () จะตั้งค่าสถานะการขัดจังหวะของเธรด หลังจากรหัสที่ทำงานในเธรดที่ถูกขัดจังหวะสามารถสำรวจสถานะการขัดจังหวะเพื่อดูว่ามีการร้องขอให้หยุดสิ่งที่กำลังทำอยู่หรือไม่ สถานะการขัดจังหวะสามารถอ่านได้โดย thread.isinterrupted () และสามารถอ่านและเคลียร์ได้โดยการดำเนินการที่ชื่อว่า thread.interrupted ()
การหยุดชะงักเป็นกลไกการทำงานร่วมกัน เมื่อเธรดหนึ่งขัดจังหวะเธรดอื่นเธรดที่ถูกขัดจังหวะไม่จำเป็นต้องหยุดสิ่งที่กำลังทำอยู่ทันที แต่การขัดจังหวะเป็นการร้องขอที่สุภาพไปยังเธรดอื่นเพื่อหยุดสิ่งที่กำลังทำเมื่อมันเต็มใจและสะดวกสบาย บางวิธีเช่น thread.sleep () ให้การร้องขอดังกล่าวอย่างจริงจัง แต่แต่ละวิธีไม่จำเป็นต้องตอบสนองต่อการขัดจังหวะ สำหรับคำขอขัดจังหวะวิธีการที่ไม่บล็อก แต่ยังคงใช้เวลานานในการดำเนินการสามารถสำรวจสถานะการขัดจังหวะและส่งคืนล่วงหน้าเมื่อถูกขัดจังหวะ คุณสามารถเพิกเฉยต่อคำขอขัดจังหวะได้ แต่การทำเช่นนั้นจะส่งผลกระทบต่อการตอบกลับ
ประโยชน์อย่างหนึ่งของลักษณะการทำงานร่วมกันของการหยุดชะงักคือมันให้ความยืดหยุ่นมากขึ้นในการสร้างกิจกรรมที่ยกเลิกได้อย่างปลอดภัย เราไม่ค่อยต้องการกิจกรรมที่จะหยุดทันที หากกิจกรรมถูกยกเลิกในขณะที่การอัปเดตกำลังดำเนินการโครงสร้างข้อมูลโปรแกรมอาจไม่สอดคล้องกัน การขัดจังหวะช่วยให้กิจกรรมที่ยกเลิกได้ในการทำความสะอาดงานต่อเนื่องคืนค่าค่าคงที่แจ้งกิจกรรมอื่น ๆ ว่าจะถูกยกเลิกก่อนที่จะถูกยกเลิก
จัดการ InterruptedException
หากการขว้าง InterruptedException หมายความว่าวิธีการเป็นวิธีการปิดกั้นการเรียกใช้วิธีการบล็อกหมายความว่าวิธีการของคุณยังเป็นวิธีการบล็อกและคุณควรมีกลยุทธ์บางอย่างในการจัดการการขัดจังหวะ กลยุทธ์ที่ง่ายที่สุดคือการโยนการขัดจังหวะตัวเองด้วยตัวเองดังที่แสดงในรหัสในวิธีการ Puttask () และ getTask () ในรายการ 1. การทำเช่นนั้นทำให้วิธีการตอบสนองต่อการขัดจังหวะและเพิ่มการขัดจังหวะในประโยคการโยน
รายชื่อ 1. ไม่จับ interruptedexception เผยแพร่ไปยังผู้โทร
TaskQueue ชั้นเรียนสาธารณะ {ส่วนตัวคงที่ int max_tasks = 1000; Private BlockingQueue <Task> queue = new LinkedBlockingQueue <Task> (MAX_TASKS); โมฆะสาธารณะ Puttask (Task R) พ่น InterruptedException {queue.put (r); } งานสาธารณะ getTask () พ่น InterruptedException {return queue.take (); - บางครั้งจำเป็นต้องมีการทำความสะอาดบางอย่างก่อนที่จะมีการเผยแพร่ข้อยกเว้น ในกรณีนี้คุณสามารถตรวจสอบการขัดจังหวะการทำความสะอาดแล้วโยนข้อยกเว้น รายการ 2 แสดงให้เห็นถึงเทคนิคนี้ซึ่งเป็นกลไกที่ใช้ในการจับคู่ผู้เล่นในบริการเกมออนไลน์ MatchPlayers () วิธีการรอให้ผู้เล่นสองคนมาถึงจากนั้นเริ่มเกมใหม่ หากวิธีการถูกขัดจังหวะเมื่อผู้เล่นคนหนึ่งมาถึง แต่ผู้เล่นคนอื่นยังไม่มาถึงมันจะทำให้ผู้เล่นคนนั้นกลับมาอยู่ในคิวและถูกบุกรุกใหม่อีกครั้งเพื่อให้การร้องขอของผู้เล่นในเกมจะไม่หายไป
รายการ 2. ดำเนินงานการล้างข้อมูลเฉพาะงานก่อนที่จะทำการทดสอบใหม่ InterruptedException
นักเล่นชั้นเรียนสาธารณะ {ผู้เล่นส่วนตัวผู้เล่น PlayerMatcher สาธารณะ (ผู้เล่น Playersource) {this.players = ผู้เล่น; } public void matchplayers () พ่น InterruptedException {ลอง {player playerone, playertwo; ในขณะที่ (จริง) {playerOne = playertWo = null; // รอให้ผู้เล่นสองคนมาถึงและเริ่มเล่นเกมใหม่ = ผู้เล่น waitforplayer (); // สามารถโยน IE playertwo = players.waitforplayer (); // สามารถโยน IE startNewGame (PlayerOne, Playertwo); }} catch (interruptedException e) {// ถ้าเรามีผู้เล่นหนึ่งคนและถูกขัดจังหวะให้ผู้เล่นคนนั้นกลับมาถ้า (ผู้เล่น! = null) ผู้เล่น. addfirst (playerOne); // จากนั้นเผยแพร่ข้อยกเว้นโยน e; - อย่าหยุดกลืนชีวิต
บางครั้งการขว้างการขัดจังหวะการรับรู้ไม่เหมาะสมตัวอย่างเช่นเมื่องานที่กำหนดโดยการเรียกใช้วิธีการที่ไม่สามารถขัดจังหวะได้ ในกรณีนี้การขัดจังหวะการรับรู้ไม่สามารถถูกโยนอีกครั้ง แต่คุณไม่ต้องการทำอะไรเช่นกัน เมื่อวิธีการปิดกั้นตรวจพบการขัดจังหวะและโยนการขัดจังหวะการรับรู้มันจะล้างสถานะการขัดจังหวะ หากการขัดจังหวะถูกจับ แต่ไม่สามารถถูกโยนอีกครั้งหลักฐานของการขัดจังหวะที่เกิดขึ้นควรถูกเก็บไว้เพื่อให้รหัสระดับที่สูงขึ้นในสแต็กการโทรสามารถรู้การขัดจังหวะและตอบสนองต่อมัน งานนี้สามารถทำได้โดยการเรียก interrupt () ถึง "reinterrupt" เธรดปัจจุบันดังที่แสดงในรายการ 3 อย่างน้อยเมื่อใดก็ตามที่มีการจับ InterruptedException และมันจะไม่ถูกโยนอีกครั้งเธรดปัจจุบันจะถูกขัดจังหวะอีกครั้งก่อนที่จะกลับมา
รายชื่อ 3. การกลับมาทำงานต่อสถานะที่ถูกขัดจังหวะหลังจากจับภาพ InterruptedException
Taskrunner ชั้นเรียนสาธารณะใช้งาน Runnable {Private BlockingQueue <Task> คิว; Public TaskRunner (BlockingQueue <Task> คิว) {this.queue = คิว; } โมฆะสาธารณะเรียกใช้ () {ลอง {ในขณะที่ (จริง) {task task = queue.take (10, timeUnit.seconds); task.execute (); }} catch (interruptedException e) {// คืนค่าเธรดสถานะขัดจังหวะ currentthread () interrupt (); - สิ่งที่เลวร้ายที่สุดที่ต้องทำเมื่อต้องรับมือกับ InterruptedException คือการกลืนมันดิบ-จับมันแล้วก็ไม่โยนมันอีกครั้งหรือยืนยันสถานะการขัดจังหวะของเธรดอีกครั้ง สำหรับข้อยกเว้นที่คุณไม่ทราบวิธีการจัดการวิธีที่เป็นมาตรฐานที่สุดในการจัดการคือการจับและบันทึก แต่วิธีนี้ยังคงเหมือนการขัดจังหวะดิบเพราะรหัสระดับที่สูงขึ้นในสแต็กการโทรยังไม่สามารถรับข้อมูลเกี่ยวกับข้อยกเว้นได้ (ไม่ควรที่จะบันทึก interruptedException เพราะมันสายเกินไปที่จะประมวลผลเมื่อมีคนมาอ่านบันทึก) รายการ 4 แสดงรูปแบบที่ใช้กันอย่างแพร่หลายซึ่งเป็นรูปแบบของการหยุดชะงักของการกลืนดิบ:
รายการ 4. การหยุดชะงักของการกลืนแบบดิบ - อย่าทำอย่างนี้
// อย่าทำ taskrunner ในชั้นเรียนสาธารณะนี้ใช้งาน {private blockingqueue <task> คิว; Public TaskRunner (BlockingQueue <Task> คิว) {this.queue = คิว; } โมฆะสาธารณะเรียกใช้ () {ลอง {ในขณะที่ (จริง) {task task = queue.take (10, timeUnit.seconds); task.execute (); }} catch (interruptedException swallowed) { / * อย่าทำสิ่งนี้ - คืนสถานะการขัดจังหวะแทน * /}}}} หากการขัดจังหวะไม่สามารถถูกโยนอีกครั้งโดยไม่คำนึงว่าคุณวางแผนที่จะประมวลผลคำขอขัดจังหวะหรือไม่คุณยังต้อง interrupt เธรดปัจจุบันอีกครั้งเนื่องจากคำขอขัดจังหวะหนึ่งครั้งอาจมี "ผู้รับ" หลายตัว พูลเธรดมาตรฐาน (ThreadPoolexecutor) การใช้งานเธรดคนงานมีหน้าที่รับผิดชอบในการหยุดชะงักดังนั้นการขัดจังหวะงานในพูลเธรดที่กำลังทำงานสามารถมีเอฟเฟกต์คู่ หนึ่งคือการยกเลิกงานและอื่น ๆ คือการแจ้งเธรดการดำเนินการว่าพูลเธรดกำลังจะปิด หากงานกินคำขอเธรดผู้ปฏิบัติงานจะไม่ทราบว่ามีการขัดจังหวะที่ร้องขอล่าช้าการปิดแอปพลิเคชันหรือบริการ
ใช้งานที่ถูกยกเลิก
ไม่มีความหมายเฉพาะสำหรับการขัดจังหวะในข้อกำหนดภาษา แต่ในโปรแกรมขนาดใหญ่มันเป็นเรื่องยากที่จะรักษาความหมายขัดจังหวะใด ๆ ยกเว้นการยกเลิก ผู้ใช้สามารถขอยกเลิกผ่าน GUI หรือผ่านกลไกเครือข่ายเช่น JMX หรือบริการเว็บ ตรรกะของโปรแกรมสามารถขอยกเลิกได้ ตัวอย่างเช่น Web Crawler จะปิดตัวเองโดยอัตโนมัติหากตรวจพบว่าดิสก์เต็มมิฉะนั้นอัลกอริทึมแบบขนานจะเริ่มเธรดหลายตัวเพื่อค้นหาพื้นที่ที่แตกต่างกันของพื้นที่โซลูชันและยกเลิกเธรดเหล่านั้น
เพียงเพราะงานถูกยกเลิกไม่ได้หมายความว่าจะต้องมีการตอบสนองการร้องขอการขัดจังหวะทันที สำหรับงานที่เรียกใช้รหัสในลูปมักจะจำเป็นต้องตรวจสอบการขัดจังหวะครั้งเดียวสำหรับการวนซ้ำแต่ละครั้ง ขึ้นอยู่กับระยะเวลาที่ลูปถูกดำเนินการอาจใช้เวลาสักครู่เพื่อให้รหัสใด ๆ สังเกตเห็นว่าเธรดถูกขัดจังหวะ (โพลของสถานะอินเตอร์รัปต์โดยเรียกเมธอด thread.isinterrupt () หรือเรียกวิธีการบล็อก) หากงานจำเป็นต้องตอบสนองก็สามารถสำรวจความคิดเห็นของรัฐขัดจังหวะได้บ่อยขึ้น วิธีการปิดกั้นมักจะสำรวจสถานะการขัดจังหวะทันทีที่ทางเข้าและหากมีการตั้งค่าเพื่อปรับปรุงการตอบสนองมันก็จะเกิดการขัดจังหวะ
ครั้งเดียวที่คุณสามารถหยุดกลืนได้คือคุณรู้ว่าเธรดกำลังจะออก สถานการณ์นี้จะเกิดขึ้นก็ต่อเมื่อคลาสที่เรียกว่าวิธีการขัดจังหวะเป็นส่วนหนึ่งของเธรดไม่สามารถเรียกใช้งานได้หรือรหัสไลบรารีทั่วไปดังแสดงในรายการ 5 รายการ 5 สร้างเธรดที่แสดงหมายเลขสำคัญจนกว่าจะถูกขัดจังหวะซึ่งได้รับอนุญาตให้ออกเมื่อถูกขัดจังหวะ ลูปที่ใช้ในการค้นหาการตรวจสอบการหยุดชะงักในสองสถานที่: หนึ่งคือการสำรวจวิธีการ isinterrupted () ที่หัวของลูปในขณะที่และอื่น ๆ คือการเรียกวิธีการบล็อกการบล็อกคิวบิวบิ๊ก ()
รายการ 5. หากคุณรู้ว่าเธรดกำลังจะออกคุณสามารถกลืนการขัดจังหวะได้
PrimeProducer ระดับสาธารณะขยายเธรด {ส่วนตัวการปิดกั้นสุดท้าย <piginteger> คิว; PrimeProducer (blockingqueue <biginteger> คิว) {this.queue = queue; } public void run () {ลอง {biginteger p = biginteger.one; ในขณะที่ (! tread.currentthread (). isinterrupted ()) queue.put (p = p.nextprobablePrime ()); } catch (interruptedException บริโภค) { / * อนุญาตให้เธรดออกจาก * /}} โมฆะสาธารณะยกเลิก () {interrupt (); - วิธีการปิดกั้นที่ไม่แตกหัก
ไม่ใช่วิธีการบล็อกทั้งหมดที่ทำให้เกิดการขัดจังหวะ บล็อกอินพุตและเอาท์พุทคลาสบล็อกที่รอให้ I/O ดำเนินการให้เสร็จสมบูรณ์ แต่พวกเขาจะไม่โยนการขัดจังหวะการรับรู้และจะไม่กลับมาล่วงหน้าหากถูกขัดจังหวะ อย่างไรก็ตามสำหรับซ็อกเก็ต I/O หากเธรดปิดซ็อกเก็ตการปิดกั้นการทำงานของ I/O บนซ็อกเก็ตนั้นจะจบลงก่อนและ socketexception จะถูกโยนลงไป คลาส I/O ที่ไม่ปิดกั้นใน Java.nio ไม่รองรับ I/O ที่ถูกขัดจังหวะ แต่พวกเขายังสามารถยกเลิกการปิดกั้นได้โดยการปิดช่องทางหรือขอปลุกในตัวเลือก ในทำนองเดียวกันการพยายามรับการล็อคภายใน (การเข้าสู่บล็อกที่ซิงโครไนซ์) ไม่สามารถถูกขัดจังหวะได้
งานที่ไม่สามารถตอบได้
งานบางอย่างปฏิเสธที่จะถูกขัดจังหวะซึ่งทำให้พวกเขาไม่ถูกคุมขัง อย่างไรก็ตามแม้งานที่ไม่สามารถใดได้ควรพยายามรักษาสถานะการขัดจังหวะในกรณีที่รหัสระดับสูงกว่าในสแต็กการโทรจำเป็นต้องดำเนินการขัดจังหวะหลังจากงานที่ไม่สามารถคุกคามได้ รายการ 6 แสดงวิธีการที่รอคิวการบล็อกจนกว่ารายการที่มีจะปรากฏในคิวไม่ว่าจะถูกขัดจังหวะหรือไม่ก็ตาม เพื่อความสะดวกมันจะดำเนินการต่อสถานะขัดจังหวะหลังจากสิ้นสุดในที่สุดบล็อกในที่สุดเพื่อไม่ให้กีดกันผู้โทรของคำขอขัดจังหวะ (ไม่สามารถดำเนินการต่อสถานะขัดจังหวะก่อนหน้านี้ได้เพราะนั่นจะทำให้เกิดการวนรอบที่ไม่มีที่สิ้นสุด - blockingqueue.take () จะสำรวจสถานะการขัดจังหวะที่ทางเข้าทันทีและหากพบว่าชุดสถานะอินเตอร์รัปต์พบว่าการขัดจังหวะจะถูกโยนลงไป)
รายการ 6. งานที่ไม่สามารถส่งได้ที่เรียกคืนสถานะที่ถูกขัดจังหวะก่อนที่จะกลับมา
งานสาธารณะ getNextTask (blockingqueue <task> คิว) {บูลีนขัดจังหวะ = false; ลอง {ในขณะที่ (จริง) {ลอง {return queue.take (); } catch (interruptedException e) {interrupted = true; // ตกผ่านและลอง retry}}} ในที่สุด {ถ้า (ขัดจังหวะ) เธรด currentthread () interrupt (); - สรุป
คุณสามารถใช้กลไกการขัดจังหวะการทำงานร่วมกันที่จัดทำโดยแพลตฟอร์ม Java เพื่อสร้างกลยุทธ์การยกเลิกที่ยืดหยุ่น กิจกรรมสามารถตัดสินใจขึ้นอยู่กับดุลยพินิจของตนเองว่าพวกเขาสามารถยกเลิกได้หรือไม่สามารถคุกคามได้และวิธีการตอบสนองต่อการขัดจังหวะและยังสามารถเลื่อนการขัดจังหวะหากผลตอบแทนทันทีจะส่งผลต่อความสมบูรณ์ของแอปพลิเคชัน แม้ว่าคุณต้องการเพิกเฉยต่อการขัดจังหวะอย่างสมบูรณ์ในรหัสของคุณคุณควรตรวจสอบให้แน่ใจว่าสถานะการขัดจังหวะจะถูกกู้คืนโดยไม่ต้องโยนอีกครั้งเพื่อให้รหัสที่เรียกว่าไม่ทราบว่าเกิดอะไรขึ้นกับการขัดจังหวะ ข้างต้นเป็นเนื้อหาเต็มรูปแบบของทฤษฎีและการปฏิบัติของการจัดการข้อยกเว้นการขัดจังหวะ ฉันหวังว่าบทความนี้จะเป็นประโยชน์กับคุณ หากคุณมีคำถามใด ๆ โปรดฝากข้อความเพื่อสนทนา