1. การวิเคราะห์ซอร์สโค้ดคลาสที่ไม่ปลอดภัย
คลาสที่ไม่ปลอดภัยในแพ็คเกจ RT.JAR ของ JDK ให้การดำเนินการอะตอมระดับฮาร์ดแวร์ วิธีการที่ไม่ปลอดภัยเป็นวิธีการดั้งเดิมทั้งหมดและห้องสมุดการใช้งาน C ++ ท้องถิ่นสามารถเข้าถึงได้โดยใช้ JNI
คำอธิบายของฟังก์ชั่นหลักของคลาสที่ไม่ปลอดภัยใน RT.JAR คลาสที่ไม่ปลอดภัยให้การดำเนินการอะตอมระดับฮาร์ดแวร์และสามารถใช้งานตัวแปรหน่วยความจำได้อย่างปลอดภัยโดยตรง มีการใช้กันอย่างแพร่หลายในซอร์สโค้ด JUC การทำความเข้าใจหลักการของมันวางรากฐานสำหรับการศึกษาซอร์สโค้ด JUC
ก่อนอื่นให้เข้าใจการใช้วิธีการหลักในชั้นเรียนที่ไม่ปลอดภัยดังนี้:
1. วิธีการ ObjectFieldEffset (ฟิลด์ฟิลด์): ส่งคืนที่อยู่ออฟเซ็ตหน่วยความจำของตัวแปรที่ระบุในคลาสที่เป็นของมัน ที่อยู่ออฟเซ็ตจะใช้เฉพาะเมื่อเข้าถึงฟิลด์ที่ระบุในฟังก์ชันที่ไม่ปลอดภัย รหัสต่อไปนี้ใช้ unsafe เพื่อรับหน่วยความจำออฟเซ็ตของค่าตัวแปรใน atomiclong ในวัตถุ Atomiclong รหัสมีดังนี้:
คงที่ {ลอง {valueOffSet = unsafe.ObjectFieldOffset (Atomiclong.class.getDeclaredField ("ค่า")); } catch (exception ex) {โยนข้อผิดพลาดใหม่ (ex); -2. วิธีการ arraybaseoffset (คลาส ArrayClass): รับที่อยู่ขององค์ประกอบแรกในอาร์เรย์
3. วิธีการ arrayIndexScale (คลาส ArrayClass): รับจำนวนไบต์ที่ถูกครอบครองโดยองค์ประกอบเดียวในอาร์เรย์
3.Boolean เปรียบเทียบและ Object OBJ, ชดเชยยาว, คาดหวังนาน, การอัปเดตยาว): เปรียบเทียบว่าค่าของตัวแปรของออฟเซ็ตชดเชยใน Object OBJ นั้นเท่ากับที่คาดหวังหรือไม่ หากเท่ากันจะได้รับการอัปเดตด้วยค่าการอัปเดตแล้วส่งคืนจริงมิฉะนั้นจะส่งคืนเท็จ
4. Public Native Long Long Getlongvolative (Object OBJ, Long Offset): รับค่าของความหมายหน่วยความจำที่ผันผวนซึ่งสอดคล้องกับตัวแปรของออฟเซ็ตออฟเซ็ตใน Object OBJ
5.Void PutorderEdlong (Object OBJ, Offset ยาว, ค่ายาว): ตั้งค่าของฟิลด์ยาวที่สอดคล้องกับที่อยู่ออฟเซ็ตออฟเซ็ตในวัตถุ OBJ เป็นค่า นี่คือวิธีการ putlongvolatile ที่มีความล่าช้าและไม่รับประกันว่าการปรับเปลี่ยนค่าจะมองเห็นได้ทันทีในเธรดอื่น ๆ ตัวแปรจะมีประโยชน์ก็ต่อเมื่อมีการแก้ไขด้วยความผันผวนและคาดว่าจะได้รับการแก้ไขโดยไม่คาดคิด
6.Void Park (บูลีน isabsolute, ใช้เวลานาน) วิธี: บล็อกเธรดปัจจุบัน เมื่อพารามิเตอร์ isabsolute เท่ากับเท็จเวลาเท่ากับ 0 หมายถึงการบล็อกตลอดเวลา เวลาที่มากกว่า 0 หมายความว่าเธรดการบล็อกจะถูกปลุกขึ้นหลังจากรอเวลาที่กำหนด เวลานี้เป็นค่าสัมพัทธ์ค่าที่เพิ่มขึ้นนั่นคือเธรดปัจจุบันจะถูกปลุกขึ้นมาหลังจากการสะสมเวลาเมื่อเทียบกับเวลาปัจจุบัน หาก Isabsolute เท่ากับความจริงและเวลามากกว่า 0 นั่นหมายความว่ามันจะถูกปลุกขึ้นมาหลังจากปิดกั้นจุดเวลาที่กำหนด เวลาที่นี่เป็นเวลาที่แน่นอนซึ่งเป็นค่าที่แปลงเป็น MS ในเวลาที่กำหนด นอกจากนี้เมื่อเธรดอื่น ๆ เรียกวิธีการขัดจังหวะของเธรดบล็อกปัจจุบันและขัดจังหวะเธรดปัจจุบันเธรดปัจจุบันจะกลับมาเช่นกัน เมื่อเธรดอื่นเรียกวิธี unpark และใช้เธรดปัจจุบันเป็นพารามิเตอร์เธรดปัจจุบันจะกลับมาด้วย
7.Void unpark (เธรดวัตถุ): ปลุกเธรดการบล็อกหลังจากการเรียกพาร์คและพารามิเตอร์เป็นเธรดที่ต้องตื่นขึ้นมา
มีการเพิ่มวิธีการใหม่หลายอย่างใน JDK1.8 นี่คือรายการง่าย ๆ ของวิธีการใช้งานประเภทยาวดังนี้:
8. ยาว getandsetlong (Object OBJ, ชดเชยยาว, การอัปเดตยาว) วิธี: รับค่าของความหมายผันผวนของตัวแปรที่มีออฟเซ็ตใน Object OBJ และตั้งค่าของความหมายผันผวนของตัวแปรเพื่ออัปเดต วิธีการใช้งานมีดังนี้:
สาธารณะรอบสุดท้าย Long Getandsetlong (Object OBJ, Offset ยาว, การอัปเดตยาว) {Long L; ทำ {l = getLongvolatile (obj, ออฟเซ็ต); // (1)} ในขณะที่ (! เปรียบเทียบและ swaplong (obj, ออฟเซ็ต, l, update)); กลับ l; -จากรหัสภายใน (1) คุณสามารถใช้ getLongvolative เพื่อรับค่าของตัวแปรปัจจุบันจากนั้นใช้การดำเนินการอะตอม CAS เพื่อตั้งค่าใหม่ ที่นี่การใช้ในขณะที่ลูปคำนึงถึงสถานการณ์ที่หลายเธรดเรียกในเวลาเดียวกันจากนั้นจำเป็นต้องใช้การสปินรีดเรีรีหลังจาก CAS ล้มเหลว
9. ยาว getandaddlong (Object OBJ, Offset ยาว, addValue ยาว): รับค่าของความหมายของความผันผวนของตัวแปรด้วยการชดเชยใน Object OBJ และตั้งค่าตัวแปรเป็นค่าดั้งเดิม + addValue วิธีการใช้งานมีดังนี้:
สาธารณะรอบชิงชนะเลิศ Long Getandaddlong (Object OBJ, ชดเชยยาว, addValue ยาว) {long l; ทำ {l = getLongvolatile (OBJ, ออฟเซ็ต); } ในขณะที่ (! เปรียบเทียบและ swaplong (obj, ชดเชย, l, l + addValue)); กลับ l; -เช่นเดียวกับการใช้งานของ getandsetlong ยกเว้นว่าเมื่อใช้ CAS ที่นี่จะใช้ค่าดั้งเดิม + ค่าของพารามิเตอร์ที่เพิ่มขึ้นของพารามิเตอร์ที่เพิ่มขึ้น
ดังนั้นจะใช้คลาสที่ไม่ปลอดภัยได้อย่างไร?
เห็นว่าไม่ปลอดภัยนั้นยอดเยี่ยมมากคุณต้องการฝึกซ้อมหรือไม่? โอเคก่อนอื่นให้ดูที่รหัสต่อไปนี้:
แพ็คเกจ com.hjc; นำเข้า sun.misc.unsafe;/*** สร้างโดย cong เมื่อปี 2018/6/6 */คลาสสาธารณะ testunsafe {// รับอินสแตนซ์ของ unsafe (2.2.1) สุดท้าย unsafe unsafe = unsafe.getUnsafe (); // บันทึกค่าออฟเซ็ตของสถานะตัวแปรในคลาส testunsafe (2.2.2) คงที่สุดท้ายคงที่ stateoffset ยาว; // ตัวแปร (2.2.3) สถานะยาวผันผวนส่วนตัว = 0; คงที่ {ลอง {// รับค่าออฟเซ็ตของตัวแปรสถานะในคลาส testunsafe (2.2.4) stateoffset = unsafe.objectfieldoffset (testunsafe.class.getDeclaredField ("State")); } catch (exception ex) {system.out.println (ex.getLocalizedMessage ()); โยนข้อผิดพลาดใหม่ (อดีต); }} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {// สร้างอินสแตนซ์และตั้งค่าสถานะเป็น 1 (2.2.5) ทดสอบทดสอบ = testunSafe ใหม่ (); //(2.2.6) Boolean SUCESS = unSafe.compareandswapint (ทดสอบ, StateOffset, 0, 1); System.out.println (ความสำเร็จ); -รหัส (2.2.1) ได้รับอินสแตนซ์ของความไม่ปลอดภัยและรหัส (2.2.3) สร้างสถานะตัวแปรที่เริ่มต้นเป็น 0
รหัส (2.2.4) ใช้ UNSARE.ObjectFieldOffset เพื่อรับที่อยู่ออฟเซ็ตหน่วยความจำของตัวแปรสถานะในคลาส TestunSafe ในวัตถุ TestUnSafe และบันทึกลงในตัวแปร STATEOFFSET
รหัส (2.2.6) เรียกใช้วิธีการเปรียบเทียบแรนด์สวินต์ของอินสแตนซ์ที่ไม่ปลอดภัยที่สร้างขึ้นและตั้งค่าของตัวแปรสถานะของวัตถุทดสอบ โดยเฉพาะอย่างยิ่งหากตัวแปรสถานะที่มีการชดเชยหน่วยความจำของวัตถุทดสอบคือ stateoffset คือ 0 ค่าการอัปเดตจะถูกเปลี่ยนเป็น 1
เราต้องการป้อน TRUE ในรหัสข้างต้น แต่หลังจากดำเนินการผลลัพธ์ต่อไปนี้จะถูกส่งออก:
ทำไมสิ่งนี้ถึงเกิดขึ้น? คุณต้องป้อนรหัส getunsafe เช่นดูว่ามีอะไรทำในนั้น:
เอกชนคงที่สุดท้ายไม่ปลอดภัย Theunsafe = ใหม่ unsafe (); สาธารณะคงที่ไม่ปลอดภัย getUnsafe () {// (2.2.7) คลาส localClass = reflection.getCallerClass (); // (2.2.8) ถ้า (! vm.issystemdomainloader (localclass.getclassloader ())) {โยนความปลอดภัยใหม่ ("unsafe"); } return theunsafe;} // ตัดสินว่า paramclassloader เป็นตัวโหลดคลาส bootstrap (2.2.9) บูลีนสาธารณะ issystemdomainloader (classloader paramclassloader) {return paramclassloader == null; -รหัส (2.2.7) รับวัตถุคลาสของวัตถุที่เรียก getunsafe นี่คือ testunsafe.cals
รหัส (2.2.8) กำหนดว่าเป็น LocalClass ที่โหลดโดยตัวโหลดคลาส bootstrap หรือไม่ กุญแจสำคัญในที่นี้คือว่า bootstrap loader โหลด testunsafe.class หรือไม่ ผู้ที่ได้เห็นกลไกการโหลดคลาสของ Java Virtual Machine เห็นได้ชัดว่าเป็นเพราะ TestunSafe.Class โหลดโดยใช้ AppClassLoader ดังนั้นข้อยกเว้นจะถูกโยนลงที่นี่โดยตรง
ดังนั้นคำถามคือทำไมคุณต้องตัดสินใจนี้?
เรารู้ว่าคลาสที่ไม่ปลอดภัยนั้นมีให้ใน RT.JAR และคลาสใน RT.JAR นั้นโหลดโดยใช้ตัวโหลดคลาส bootstrap คลาสที่เราเริ่มต้นฟังก์ชั่นหลักจะถูกโหลดโดยใช้ AppClassLoader ดังนั้นเมื่อโหลดคลาสที่ไม่ปลอดภัยในฟังก์ชั่นหลักโดยพิจารณาว่ากลไกการมอบหมายหลักจะมอบหมายให้ Bootstrap โหลดคลาสที่ไม่ปลอดภัย
หากไม่มีการตรวจสอบรหัส (2.2.8) แอปพลิเคชันของเราสามารถใช้สิ่งที่ไม่ปลอดภัยในการทำสิ่งต่าง ๆ ได้ คลาสที่ไม่ปลอดภัยสามารถใช้งานหน่วยความจำได้โดยตรงซึ่งไม่ปลอดภัยมาก ดังนั้นทีมพัฒนา JDK จึงมีข้อ จำกัด นี้เป็นพิเศษไม่อนุญาตให้นักพัฒนาใช้คลาสที่ไม่ปลอดภัยภายใต้ช่องทางปกติ แต่ใช้ฟังก์ชั่นที่ไม่ปลอดภัยในคลาสหลักใน RT.JAR
คำถามคือเราควรทำอย่างไรถ้าเราต้องการยกตัวอย่างคลาสที่ไม่ปลอดภัยและใช้ฟังก์ชั่นที่ไม่ปลอดภัย
เราไม่ควรลืมเทคโนโลยีการสะท้อนแสงสีดำและใช้การสะท้อนที่เป็นสากลเพื่อให้ได้วิธีการอินสแตนซ์ของ Unsafe รหัสมีดังนี้:
แพ็คเกจ com.hjc; นำเข้า sun.misc.unsafe; นำเข้า java.lang.reflect.field;/*** สร้างโดย cong เมื่อปี 2018/6/6 */คลาสสาธารณะ testunsafe {คงที่สุดท้ายไม่ปลอดภัยไม่ปลอดภัย; Stateoffset Long Long Final; สถานะยาวผันผวนส่วนตัว = 0; Static {ลอง {// ไตร่ตรองเพื่อรับตัวแปรสมาชิก Theunsafe Theunsafe (2.2.10) ฟิลด์ = unsafe.class.getDeclaredField ("theunsafe"); // ตั้งค่าเป็น field (2.2.11) field.setAccessible (จริง); // รับค่าของตัวแปรนี้ (2.2.12) unsafe = (unsafe) ฟิลด์ (null); // รับออฟเซ็ตของสถานะใน testunsafe (2.2.13) stateoffset = unsafe.objectfieldoffset (testunsafe.class.getDeclaredField ("State")); } catch (exception ex) {system.out.println (ex.getLocalizedMessage ()); โยนข้อผิดพลาดใหม่ (อดีต); }} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {testunsafe test = new TestunSafe (); ความสำเร็จของบูลีน = unsafe.compareandswapint (ทดสอบ, stateoffset, 0, 1); System.out.println (ความสำเร็จ); -หากรหัสข้างต้น (2.2.10 2.2.11 2.2.12) สะท้อนตัวอย่างของความไม่ปลอดภัยผลการทำงานจะมีดังนี้:
2. การวิจัยเกี่ยวกับรหัสแหล่งที่มาของคลาส Locksupport
Locksupport ใน RT.JAR ใน JDK เป็นคลาสเครื่องมือและฟังก์ชั่นหลักของมันคือการระงับและปลุกเธรด มันเป็นพื้นฐานสำหรับการสร้างล็อคและคลาสการซิงโครไนซ์อื่น ๆ
คลาส Locksupport จะเชื่อมโยงกับแต่ละเธรดที่ใช้ เธรดที่เรียกวิธีการของคลาส Locksupport โดยค่าเริ่มต้นไม่ถือใบอนุญาต Locksupport ถูกนำไปใช้ภายในโดยใช้คลาสที่ไม่ปลอดภัย
ที่นี่เราควรให้ความสนใจกับฟังก์ชั่นที่สำคัญหลายประการของ Locksupport ดังต่อไปนี้:
1.Void Park () วิธี: หาก Thread Calling Park () ได้รับใบอนุญาตที่เกี่ยวข้องกับ LocksUpport จากนั้นโทร LocksUpport.park () จะกลับมาทันที มิฉะนั้นเธรดการโทรจะถูกห้ามไม่ให้เข้าร่วมในการกำหนดเวลาของเธรดนั่นคือมันจะถูกบล็อกและระงับ รหัสต่อไปนี้เป็นตัวอย่างต่อไปนี้:
แพ็คเกจ com.hjc; นำเข้า java.util.concurrent.locks.locksupport;/*** สร้างโดย cong เมื่อปี 2018/6/6 */คลาสสาธารณะ locksupporttest {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println ("พาร์คสตาร์ท!"); lockksupport.park (); System.out.println ("Park Stop!"); -ดังที่แสดงในรหัสด้านบนเรียกใช้วิธีการอุทยานโดยตรงในฟังก์ชั่นหลักและผลลัพธ์สุดท้ายจะส่งออกเฉพาะพาร์คเริ่มต้นเท่านั้น! จากนั้นเธรดปัจจุบันจะถูกระงับเนื่องจากเธรดการโทรไม่ถือใบอนุญาตตามค่าเริ่มต้น ผลการดำเนินการมีดังนี้:
เมื่อคุณเห็นเธรดอื่น ๆ เรียกเมธอด unpark (เธรดด้าย) และเธรดปัจจุบันจะใช้เป็นพารามิเตอร์เธรดที่เรียกว่าวิธีการพาร์คจะกลับมา นอกจากนี้เธรดอื่น ๆ เรียกวิธีการขัดจังหวะ () ของเธรดการปิดกั้น เมื่อตั้งค่าสถานะอินเตอร์รัปต์หรือเธรดการบล็อกจะกลับมาหลังจากการปลุกเท็จของเธรดจะเป็นการดีที่สุดที่จะใช้เงื่อนไขการวนซ้ำเพื่อตัดสิน
ควรสังเกตว่าเธรดที่เรียกวิธีการที่ Park () ที่ถูกบล็อกถูกขัดจังหวะโดยเธรดอื่น ๆ และการส่งคืนเธรดที่ถูกบล็อกจะไม่ส่งข้อยกเว้น InterruptedException
2.Void unpark (เธรดเธรด) เมื่อเธรดเรียก unpark หากเธรดเธรดพารามิเตอร์ไม่ถือใบอนุญาตที่เกี่ยวข้องกับเธรดและคลาส locksupport ให้เธรดถือไว้ หากเธรดที่เรียกว่า Park () ถูกระงับก่อนและเรียกเธรดเธรดจะถูกปลุกหลังจากเรียกว่า unpark
หากเธรดไม่ได้เรียกว่าพาร์คมาก่อนหลังจากเรียกใช้วิธีการที่ไม่ได้ทำอะไรแล้ววิธีการพาร์ค () จะถูกส่งคืนทันที รหัสข้างต้นได้รับการแก้ไขดังนี้:
แพ็คเกจ com.hjc; นำเข้า java.util.concurrent.locks.locksupport;/*** สร้างโดย cong เมื่อปี 2018/6/6 */คลาสสาธารณะ locksupporttest {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println ("พาร์คสตาร์ท!"); // ทำให้เธรดปัจจุบันได้รับ linctupport.unpark (thread.currentthread ()); // Call Park Lockksupport.park () อีกครั้ง; System.out.println ("Park Stop!"); -ผลการดำเนินการมีดังนี้:
ต่อไปเรากำลังมองหาตัวอย่างเพื่อให้เข้าใจถึงสวนสาธารณะที่ไม่ได้ทำอย่างลึกซึ้งยิ่งขึ้นรหัสมีดังนี้:
นำเข้า java.util.concurrent.locks.locksupport;/*** สร้างโดย Cong เมื่อปี 2018/6/6 */คลาสสาธารณะ locksupporttest {โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่น InterruptedException {เธรดเธรด = เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะเรียกใช้ () {system.out.out.println ( - // เริ่มเธรดลูก start (); // เธรดหลักจะนอน 1S ด้าย. sleep (1,000); System.out.println ("Main Thread Unpark Start!"); // โทรหา unpark เพื่อให้เธรดถือใบอนุญาตจากนั้นวิธีการอุทยานจะส่งคืน locksupport.unpark (เธรด); -ผลการดำเนินการมีดังนี้:
รหัสด้านบนสร้างเธรดลูกก่อน หลังจากเริ่มต้นเธรดเด็กเรียกวิธีการอุทยาน เนื่องจากเธรดลูกเริ่มต้นไม่ถือใบอนุญาตจึงจะแขวนอยู่
ด้ายหลักนอนเป็นเวลา 1 วินาที จุดประสงค์คือเธรดหลักเรียกวิธี unpark และให้เธรดลูกส่งออกพาร์คพาร์คเด็กเริ่มต้น! และบล็อก
เธรดหลักจากนั้นจะดำเนินการวิธี unpark โดยที่พารามิเตอร์เป็นเธรดลูกมีวัตถุประสงค์เพื่อให้เธรดลูกถือใบอนุญาตจากนั้นวิธีการที่พาร์คเรียกโดยเธรดลูกจะส่งคืน
เมื่อกลับวิธีการอุทยานมันจะไม่บอกคุณว่าเหตุผลใดที่มันกลับมา ดังนั้นผู้โทรจะต้องตรวจสอบอีกครั้งว่าเงื่อนไขเป็นไปตามวิธีการที่เขาอยู่ในการโทรในปัจจุบันหรือไม่ หากไม่ได้พบเขาต้องเรียกวิธีการอุทยานอีกครั้ง
ตัวอย่างเช่นสถานะการขัดจังหวะของเธรดเมื่อมันกลับมาสามารถพิจารณาได้ว่าจะส่งคืนหรือไม่เนื่องจากมันถูกขัดจังหวะขึ้นอยู่กับการเปรียบเทียบสถานะอินเตอร์รัปต์ก่อนและหลังการโทร
เพื่อแสดงให้เห็นว่าเธรดหลังจากเรียกวิธีการอุทยานจะกลับมาหลังจากถูกขัดจังหวะให้แก้ไขรหัสตัวอย่างด้านบนและลบ LocksUpport.unpark (เธรด); จากนั้นเพิ่ม thread.interrupt (); รหัสมีดังนี้:
นำเข้า java.util.concurrent.locks.locksupport;/*** สร้างโดย Cong เมื่อปี 2018/6/6 */คลาสสาธารณะ locksupporttest {โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่น InterruptedException {เธรดเธรด = เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะเรียกใช้ () {System.out.println ("Subthread Park เริ่มต้น!"); (! thread.currentthread (). isinterrupted ()) {locksupport.park (); // เริ่มเธรดลูก start (); // เธรดหลักนอน 1S ด้าย. sleep (1,000); System.out.println ("Main Thread Unpark Start!"); // interrupt thread thread.interrupt (); -ผลการดำเนินการมีดังนี้:
ในฐานะที่เป็นรหัสข้างต้นเธรดลูกจะสิ้นสุดหลังจากที่เธรดลูกถูกขัดจังหวะ หากเธรดลูกไม่ถูกขัดจังหวะแม้ว่าคุณจะเรียก unpark (เธรด) เธรดลูกจะไม่สิ้นสุด
3.Void Parknanos (Long Nanos) วิธีการ: คล้ายกับ Park ถ้า Thread Calling Park ได้รับใบอนุญาตที่เกี่ยวข้องกับ Locksupport จากนั้นเรียก LockksUpport.park () กลับมาทันที ความแตกต่างคือถ้าเธรดเรียกเธรดไม่ได้รับมันจะถูกระงับแล้วส่งคืนหลังจากเวลานาโน
พาร์คยังรองรับสามวิธีด้วยพารามิเตอร์ตัวบล็อก เมื่อเธรดเรียกพาร์คโดยไม่ถือใบอนุญาตและถูกบล็อกและระงับวัตถุบล็อกเกอร์จะถูกบันทึกไว้ในเธรด
ใช้เครื่องมือวินิจฉัยเพื่อสังเกตเหตุผลว่าทำไมเธรดถูกบล็อก เครื่องมือวินิจฉัยใช้วิธีการ getBlocker (เธรด) เพื่อรับวัตถุบล็อกเกอร์ ดังนั้น JDK แนะนำให้เราใช้วิธีการอุทยานกับพารามิเตอร์บล็อกเกอร์และตั้งค่าตัวบล็อกให้เป็นเช่นนี้ดังนั้นเมื่อหน่วยความจำการถ่ายโอนข้อมูลแก้ไขปัญหาเราสามารถรู้ได้ว่าคลาสใดถูกบล็อก
ตัวอย่างมีดังนี้:
นำเข้า java.util.concurrent.locks.locksupport;/*** สร้างโดย Cong เมื่อปี 2018/6/6 */คลาสสาธารณะ testPark {public void testPark () {locksupport.park (); // (1)} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {testPark testPark = new testPark (); testpark.testpark (); -ผลการดำเนินการมีดังนี้:
คุณจะเห็นว่ามันกำลังใช้การบล็อกดังนั้นเราจึงต้องใช้เครื่องมือในไดเรกทอรี JDK/bin เพื่อดู หากคุณไม่ทราบขอแนะนำให้ดูเครื่องมือตรวจสอบ JVM ก่อน
เมื่อใช้ JSTACK PID เพื่อดูสแต็กเธรดหลังจากทำงานผลลัพธ์ที่คุณเห็นมีดังนี้:
จากนั้นเราแก้ไขรหัสด้านบน (1) ดังนี้:
lockksupport.park (นี่); // (1)
เรียกใช้อีกครั้งและใช้ JStack PID เพื่อดูผลลัพธ์ดังนี้:
จะเห็นได้ว่าหลังจากวิธีการพาร์คกับตัวบล็อกสแต็กเธรดสามารถให้ข้อมูลเพิ่มเติมเกี่ยวกับการปิดกั้นวัตถุ
จากนั้นเราจะตรวจสอบซอร์สโค้ดของฟังก์ชันพาร์ค (วัตถุบล็อกเกอร์) ซอร์สโค้ดมีดังนี้:
Public Static Void Park (Object Blocker) {// รับเธรดการเรียกเธรด t = thread.currentthread (); // ตั้งค่าตัวแปรตัวบล็อก setBlocker (t, blocker); // แขวนเธรด unsafe.park (false, 0l); // ล้างตัวแปรตัวบล็อกหลังจากเปิดใช้งานเธรดเนื่องจากเหตุผลมักจะวิเคราะห์เมื่อเธรดถูกบล็อก setBlocker (t, null);}มีตัวแปรในคลาส Thread Class ผันผวนวัตถุ Parkblocker มันถูกใช้เพื่อจัดเก็บวัตถุบล็อกเกอร์ที่ส่งผ่านโดยสวนสาธารณะนั่นคือตัวแปรตัวบล็อกจะถูกเก็บไว้ในตัวแปรสมาชิกของเธรดที่เรียกวิธีการอุทยาน
4. ฟังก์ชั่นโมฆะ parknanos (วัตถุบล็อก, นาโนยาว) มีเวลาหมดเวลาเพิ่มเติมเมื่อเทียบกับสวนสาธารณะ (วัตถุบล็อก)
5.Void Parkuntil (Object Blocker, Long Dentline) ซอร์สโค้ดของ Parkuntil มีดังนี้:
Public Void Parkuntil (blocker วัตถุ, กำหนดเวลายาว) {เธรด t = thread.currentthread (); setBlocker (t, blocker); // isabsolute = true, เวลา = กำหนดเวลา; หมายความว่า unsafe.park (จริงกำหนดเวลา); setBlocker (t, null); -คุณจะเห็นว่ามันเป็นกำหนดเวลาที่กำหนดหน่วยเวลาคือมิลลิวินาทีซึ่งถูกแปลงเป็นค่าหลังจากมิลลิวินาทีจากปี 1970 ถึงจุดเวลาปัจจุบัน ความแตกต่างระหว่างสิ่งนี้กับ Parknanos (Object Blocker, Long Nanos) คือหลังคำนวณเวลานาโนที่รอคอยจากเวลาปัจจุบันในขณะที่อดีตระบุจุดเวลา
ตัวอย่างเช่นเราต้องรอจนถึง 20:34 น. ในปี 2018.06.06 จากนั้นแปลงจุดเวลานี้เป็นจำนวนมิลลิวินาทีทั้งหมดจากปี 1970 เป็นจุดนี้
ลองดูอีกตัวอย่างหนึ่งรหัสมีดังนี้:
นำเข้า java.util.queue; นำเข้า java.util.concurrent.concurrentlinkedqueue; นำเข้า java.util.concurrent.atomic.atomicboolean; นำเข้า java.util.concurrent.locks.locksupport; */ชั้นเรียนสาธารณะ Fifomutex {Private Final Atomicboolean Locked = New AtomicBoolean (FALSE); คิวสุดท้ายส่วนตัว <เธรด> บริกร = ใหม่พร้อมกันพร้อมกันกับการเชื่อมโยงกัน <stread> (); ล็อคโมฆะสาธารณะ () {บูลีนถูกขัดจังหวะ = false; เธรด current = thread.currentthread (); Waiters.Add (ปัจจุบัน); // เฉพาะเธรดของหัวของทีมสามารถรับล็อค (1) ในขณะที่ (waiters.peek ()! = current ||! locked.compareandset (เท็จ, จริง)) {locksupport.park (นี่); if (thread.interrupted ()) // (2) wasinterrupted = true; } waiters.remove (); ถ้า (ถูกขัดจังหวะ) // (3) current.interrupt (); } โมฆะสาธารณะปลดล็อค () {locked.set (เท็จ); locksupport.unpark (waiters.peek ()); -คุณจะเห็นว่านี่เป็นล็อคครั้งแรกในครั้งแรกนั่นคือเฉพาะองค์ประกอบส่วนหัวของคิวเท่านั้นที่สามารถรับได้ รหัส (1) หากเธรดปัจจุบันไม่ใช่ส่วนหัวคิวหรือล็อคปัจจุบันได้รับโดยเธรดอื่นแล้วให้โทรหาวิธีการพาร์คเพื่อระงับตัวเอง
จากนั้นรหัส (2) ทำให้การตัดสิน หากวิธีการอุทยานกลับมาเนื่องจากถูกขัดจังหวะการขัดจังหวะจะถูกละเว้นและธงขัดจังหวะจะถูกรีเซ็ตและมีเพียงธงเท่านั้นและจากนั้นพิจารณาอีกครั้งว่าเธรดปัจจุบันเป็นองค์ประกอบหัวของคิวหรือไม่ว่าล็อคนั้นได้มาจากเธรดอื่นหรือไม่ ถ้าเป็นเช่นนั้นให้โทรหาวิธีการพาร์คเพื่อแขวนตัวเองต่อไป
จากนั้นหากเครื่องหมายเป็นจริงในรหัส (3) เธรดจะถูกขัดจังหวะ คุณเข้าใจสิ่งนี้ได้อย่างไร? ในความเป็นจริงเธรดอื่น ๆ ขัดจังหวะเธรด แม้ว่าฉันจะไม่สนใจสัญญาณขัดจังหวะและเพิกเฉย แต่ก็ไม่ได้หมายความว่าเธรดอื่น ๆ ไม่สนใจธงดังนั้นฉันต้องกู้คืน
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com