บทความนี้เป็นบทสรุปของการใช้งานพื้นฐานของ Java Multithreading
บทความนี้จะอธิบายการใช้งานพื้นฐานของ Java multithreading จากด้านต่อไปนี้:
วิธีใช้มัลติเธรด
สองวิธีในการเริ่มต้นด้าย
Java มีสองวิธีในการใช้มัลติเธรด หนึ่งคือการเขียนคลาสเพื่อสืบทอดเธรดจากนั้นเขียนทับวิธีการเรียกใช้จากนั้นเรียกใช้วิธีการเริ่มต้นเพื่อเริ่มเธรด ในเวลานี้คลาสนี้จะเรียกใช้รหัสในวิธีการเรียกใช้ในแบบที่เธรดอื่น อีกวิธีหนึ่งคือการเขียนคลาสเพื่อใช้งานอินเตอร์เฟส Runnable จากนั้นใช้วิธีการทำงานของอินเตอร์เฟสจากนั้นสร้างวัตถุเธรดใช้คลาสที่ใช้อินเตอร์เฟส runnable เป็นพารามิเตอร์การก่อสร้างส่งผ่านไปยังวัตถุเธรดและในที่สุดวัตถุเธรดเรียกวิธีการเริ่มต้น
วิธีการเริ่มต้นที่นี่เป็นวิธีการที่มีฟังก์ชั่นเริ่มต้นซึ่งโทรกลับภายในวิธีการเรียกใช้ภายใน ดังนั้นเฉพาะเมื่อวิธีการเริ่มต้นที่เรียกว่าจะเริ่มเธรดอื่นวิธีการเรียกใช้จะถูกเรียกโดยตรงหรือทำงานในเธรดเดียวกันแทนที่จะเรียกใช้ในเธรดอื่น
นอกจากนี้วิธีการเริ่มต้นเพียงแค่บอกเครื่องเสมือนว่าเธรดสามารถเริ่มต้นได้ซึ่งหมายความว่าเธรดอยู่ในสถานะพร้อม แต่ก็ไม่ได้หมายความว่าการโทรเริ่มจะเริ่มทำงานทันที สิ่งนี้ต้องรอให้ JVM ตัดสินใจว่าจะดำเนินการเธรดเมื่อใด กล่าวอีกนัยหนึ่งหากมีสองเธรด A และ B การโทรเริ่มต้นก่อนและการโทร B เริ่มต้นในภายหลังมันไม่ได้หมายความว่าเธรด A รันก่อนและเธรด B จะทำงานในภายหลัง ทั้งหมดนี้ถูกกำหนดโดย JVM และสามารถพิจารณาการเริ่มต้นแบบสุ่ม
ด้านล่างเราใช้รหัสจริงเพื่อแสดงสองวิธีในการเริ่มเธรด:
ประเภทแรกคือการสืบทอดเธรด
Public Class ExampleThread ขยายเธรด {@Override โมฆะสาธารณะ Run () {super.run (); System.out.println ("นี่คือ ExampleThread ที่สืบทอดมาจากเธรด"); -รหัสทดสอบสามารถพบได้ในคลาส ExampleThreadTest ในไดเรกทอรีทดสอบ
อีกวิธีหนึ่งคือใช้อินเทอร์เฟซที่รันได้
Public Class Examplerunable ใช้งานได้ {public void run () {system.out.println ("นี่คือคลาสที่ใช้อินเตอร์เฟส runnable"); -สำหรับรหัสทดสอบคุณสามารถดูคลาส ExamplerunableTest ในไดเรกทอรีทดสอบ
วิธีรับข้อมูลบางอย่างเกี่ยวกับมัลติเธรด
หลังจากเริ่มเธรดมัลติเธรดเราหวังว่าจะได้รับข้อมูลบางอย่างเกี่ยวกับเธรดเริ่มต้นผ่าน API บางตัว JDK ให้วิธีการคลาสเธรดแก่เราเพื่อรับข้อมูลบางอย่างเกี่ยวกับเธรด
รับชื่อของเธรด
วิธีการเหล่านี้เป็นวิธีการภายในของเธรดดังนั้นเราจึงสามารถเรียกวิธีการเหล่านี้ได้สองวิธี หนึ่งคือเมื่อคลาสของเราสืบทอดเธรดเพื่อใช้มัลติเธรดเราสามารถใช้สิ่งนี้เพื่อเรียกมัน อีกวิธีหนึ่งคือเรียกใช้วิธีเหล่านี้ผ่าน tread.currentthread () อย่างไรก็ตามสองวิธีนี้แตกต่างกันในสถานการณ์การใช้งานที่แตกต่างกัน
ลองดูการใช้สองวิธีสั้น ๆ
ใช้เธรดแรก currentthread () รหัสมีดังนี้:
Public Class ExampleCurrentThread ขยายเธรด {Public ExampleCurrentThread () {System.out.println ("Print of Constructor Method:" + thread.currentthread (). getName ()); } @Override โมฆะสาธารณะ Run () {super.run (); System.out.println ("Print of Run Method:" + thread.currentthread (). getName ()); -รหัสทดสอบมีดังนี้:
Public Class ExampleCurrentThreadTest ขยาย testCase {public void testInit () พ่นข้อยกเว้น {ExampleCurrentThread Thread = New ExampleCurrentThread (); } public void testRun () พ่นข้อยกเว้น {ExampleCurrentThread Thread = New ExampleCurrentTHREAD (); thread.start (); Thread.sleep (1,000); -ผลลัพธ์มีดังนี้:
การพิมพ์วิธีตัวสร้าง: การพิมพ์วิธีการ mainrun: การพิมพ์วิธีการก่อสร้างเธรด -0: หลัก
เหตุใดเราจึงใช้ Thread.currentThread () ภายใน ExampleCurrentThread เพื่อแสดงว่าวิธีการก่อสร้างถูกพิมพ์เป็นหลักเนื่องจาก thread.currentthread () ส่งคืนข้อมูลที่ส่วนโค้ดถูกเรียกโดยเธรดนั้น เห็นได้ชัดว่าวิธีการก่อสร้างนั้นดำเนินการโดยเธรดหลักและวิธีการเรียกใช้จะดำเนินการโดยเธรดที่เราเริ่มด้วยตัวเอง เนื่องจากไม่มีชื่อจึงเป็นเธรดเริ่มต้น -0
ต่อไปเรากำลังดูการสืบทอดจากเธรดและเรียกมันด้วยสิ่งนี้
Public Class ComplexCurrentThread ขยายเธรด {Public ComplexCurrentThread () {System.out.println ("เริ่มต้น ===================="); System.out.println ("thread.currentthread (). getName =" + thread.currentthread (). getName ()); System.out.println ("this.getName () =" + this.getName ()); System.out.println ("end =================================================);} @Override Void Run () {super.run (); system.out.println ( System.out.println ("thread.currentthread (). getName =" + thread.currentthread (). getName ());รหัสทดสอบมีดังนี้:
Public Class ComplexCurrentThreadTest ขยาย TestCase {Public Void TestRun () พ่นข้อยกเว้น {ComplexCurrentThread Thread = New ComplexCurrentThread (); thread.setName ("byhieg"); thread.start (); Thread.sleep (3000); -ผลลัพธ์มีดังนี้:
เริ่มต้น =========== thread.currentThread (). getName = mainthis.getName () = Thread-0 ===================== วิ่งเริ่มต้น ============================================
ก่อนอื่นเมื่อสร้างวัตถุตัวสร้างจะยังคงดำเนินการโดยเธรดหลักดังนั้น thead.currentthread () จะได้รับชื่อของเธรดหลัก แต่วิธีนี้หมายถึงวัตถุที่เรียกวิธีการนั่นคือข้อมูลเธรดของ ComplexCurrentThread ยังไม่มีชื่อ setname ดังนั้นจึงเป็นชื่อเริ่มต้น จากนั้นวิธีการเรียกใช้เป็น thread.currentthread () หรือสิ่งนี้ส่งคืนข้อมูลเธรดด้วยชุดชื่อ Byhieg
ดังนั้น tread.currentthread อ้างถึงข้อมูลเธรดที่ดำเนินการโดยเฉพาะบล็อกรหัสนี้ ตัวสร้างจะดำเนินการโดย Main และวิธีการเรียกใช้เป็นเธรดที่เริ่มต้นและเธรดใดที่ดำเนินการรัน จากมุมมองนี้ข้อมูลที่สามารถรับได้นั้นไม่ถูกต้องเพราะถ้าเราดำเนินการสิ่งนี้ getName () ในการรัน แต่วิธีการเรียกใช้เริ่มต้นโดยเธรดอื่นเราไม่สามารถรับข้อมูลของเมืองใหม่ที่รันวิธีการรันนี้ได้ และเฉพาะคลาสที่สืบทอดมาจากเธรดสามารถมีวิธีการเช่น getName ซึ่งเป็นหายนะสำหรับภาษาคุณลักษณะของ Java ที่ไม่มีการสืบทอดหลายครั้ง สำหรับข้อมูลทั้งหมดเกี่ยวกับเธรดที่เราต้องการได้รับหลังจากนั้นเราใช้เธรด currentthread () เพื่อเรียก API
รับ ID ของเธรด
โทรหา GetId เพื่อรับบัตรประจำตัวที่ไม่ซ้ำกันของเธรด นี่เป็นเช่นเดียวกับการใช้งานของ GetName ด้านบน ไม่มีอะไรจะพูด คุณสามารถดูตัวอย่างโดยตรงและคลาสทดสอบ ExampleIdThreadTest
ตรวจสอบว่าเธรดมีชีวิตอยู่หรือไม่
ฟังก์ชั่นของวิธีการ isalive () คือการทดสอบว่าเธรดนั้นใช้งานอยู่หรือไม่ สถานะที่ใช้งานอยู่หมายความว่าเธรดได้เริ่มต้นขึ้น แต่ยังไม่สิ้นสุด นั่นคือหลังจากที่เธรดเริ่มต้นจะถือว่ามีชีวิตอยู่
มาดูตัวอย่างเฉพาะ:
คลาสสาธารณะ ALIVETHREAD ขยายเธรด {@Override โมฆะสาธารณะ Run () {super.run (); System.out.println ("มันมีชีวิตอยู่ในวิธีการเรียกใช้" + "" + thread.currentthread (). isalive ()); -วิธีการทดสอบมีดังนี้:
คลาสสาธารณะ AliveThreadTest ขยาย TestCase {Public Void TestRun () โยนข้อยกเว้น {AliveThread Thread = new AliveThread (); System.out.println ("เริ่มต้น ==" + thread.isalive ()); thread.start (); Thread.sleep (1,000); System.out.println ("end ==" + thread.isalive ()); Thread.sleep (3000); -ผลลัพธ์มีดังนี้:
เริ่มต้น == ไม่ว่าจะเป็นจริงในวิธีเท็จ == เท็จ
เราสามารถพบได้ก่อนที่จะเริ่มเธรดจะไม่ได้รับการพิจารณาและจากนั้นเมื่อมันทำงานมันก็ยังมีชีวิตอยู่ เมื่อใช้วิธีการเรียกใช้จะถูกพิจารณาว่าไม่ได้รับการพิจารณา
วิธีหยุดเธรด
ตรวจสอบว่าเธรดสิ้นสุดลงหรือไม่
JDK จัดเตรียมวิธีการบางอย่างเพื่อตรวจสอบว่าเธรดถูกยกเลิกหรือไม่ - Isinterrupted () และถูกขัดจังหวะ ()
วิธีหยุดเธรด
นี่เป็นวิธีที่สำคัญกว่าในการรับข้อมูลเธรดเนื่องจากเกี่ยวข้องกับวิธีการยกเลิกเธรด ก่อนอื่นให้ฉันพูดคุยเกี่ยวกับหลายวิธีในการยุติหัวข้อ:
ฉันจะไม่พูดถึงวิธีแรกวิธีการหยุดครั้งที่สอง () ถูกทอดทิ้งเพราะเหตุผลต่อไปนี้อาจเกิดขึ้น:
สำหรับตัวอย่างที่เฉพาะ
ประเภทที่สามคือวิธีการเลิกจ้างที่แนะนำในปัจจุบันการเรียกขัดจังหวะและจากนั้นพิจารณาว่าจะยกเลิกในวิธีการเรียกใช้หรือไม่ มีสองวิธีในการตัดสินการเลิกจ้าง หนึ่งคือวิธีการคงที่ขัดจังหวะ () ของคลาสเธรดและอีกวิธีหนึ่งคือวิธีการเป็นสมาชิกเธรด isinterrupted () มีความแตกต่างระหว่างสองวิธีนี้ วิธีแรกจะรีเซ็ตสถานะโดยอัตโนมัติ หากถูกขัดจังหวะ () ถูกเรียกสองครั้งติดต่อกันถ้ามันเป็นเท็จเป็นครั้งแรกครั้งที่สองมันจะต้องเป็นจริง และ isinterrupted () จะไม่
ตัวอย่างมีดังนี้:
คลาสสาธารณะตัวอย่าง intervendThread ขยายเธรด {@Override โมฆะสาธารณะเรียกใช้ () {super.run (); ลอง {สำหรับ (int i = 0; i <50000000; i ++) {ถ้า (interrupted ()) {system.out.println ("มันเป็นสถานะหยุดอยู่แล้วฉันต้องการออก"); โยน InterruptedException ใหม่ ("หยุด ...... "); } system.out.println ("i =" + (i + 1)); }} catch (interruptedException e) {system.out.println ("หยุดราบรื่น"); -รหัสทดสอบมีดังนี้:
คลาสสาธารณะ ExtramentInterruptHreadTest ขยาย TestCase {Public Void TestRun () โยนข้อยกเว้น {extraffINTINTINCHTHREAD Thread = ใหม่ ExtramitInterruptThread (); thread.start (); Thread.sleep (1,000); thread.interrupt (); -วิธีที่สี่เหมือนกับวิธีที่สาม ความแตกต่างเพียงอย่างเดียวคือการแทนที่ข้อยกเว้นที่โยนในรหัสข้างต้นด้วยการส่งคืน ฉันยังคงชอบที่จะโยนข้อยกเว้น มีการประมวลผลหลายรูปแบบที่นี่เช่นข้อมูลการพิมพ์ปิดหรือจับทรัพยากรแล้วโยนมันไปที่ชั้นบนอีกครั้ง
โปรดทราบว่าข้อยกเว้นที่เราโยนไปข้างต้นคือการขัดจังหวะ ที่นี่เราพูดสั้น ๆ เกี่ยวกับเหตุผลที่อาจมีการสร้างข้อยกเว้นนี้ ในกรณีของการนอนหลับของเธรดดั้งเดิมให้เรียกอินเตอร์รัปต์เพื่อยุติเธรดหรือยุติเธรดก่อนจากนั้นปล่อยให้เธรดนอนหลับ
วิธีหยุดเธรดชั่วคราว
สองวิธีต่อไปนี้มีให้ใน JDK สำหรับการหยุดเธรดและกู้คืนเธรด
วิธีการทั้งสองนี้เป็นวิธีที่ถูกทอดทิ้งเช่นวิธีการหยุดและการใช้งานของพวกเขาเหมือนกับการหยุดการระงับเธรดและการกู้คืนเธรดอย่างไร้ความปราณี เหตุผลหลักที่ทำให้ทั้งสองวิธีถูกทอดทิ้งคือ:
ประเพณีอื่น ๆ ของเธรด
การใช้เธรดพื้นฐานอื่น ๆ มีดังนี้:
ข้อตกลงเธรด
JDK ให้วิธีการให้ผลผลิต () เพื่อให้เธรดให้ทรัพยากร CPU ปัจจุบันและมอบให้กับงานอื่น ๆ เพื่อครอบครองเวลา CPU แต่นี่ก็เป็นสิ่งที่สุ่ม เป็นไปได้ว่าจะต้องใช้เวลาชิ้นหลังจากเลิกทรัพยากร
สำหรับตัวอย่างที่เฉพาะ
ตั้งค่าลำดับความสำคัญของเธรด
เราสามารถตั้งค่าลำดับความสำคัญของเธรดเพื่อให้ CPU ให้มากที่สุดเท่าที่จะทำได้เพื่อส่งทรัพยากรการดำเนินการไปยังเธรดที่มีลำดับความสำคัญสูงกว่า Java ตั้งค่า 1-10 10 ระดับลำดับความสำคัญและมีตัวแปรคงที่สามตัวเพื่อให้ระดับลำดับความสำคัญสามระดับ:
/*** ลำดับความสำคัญขั้นต่ำที่เธรดสามารถมีได้ */ สาธารณะสุดท้ายคงที่ int min_priority = 1; /*** ลำดับความสำคัญเริ่มต้นที่กำหนดให้กับเธรด */ สาธารณะสุดท้ายคงที่ int norm_priority = 5; /*** ลำดับความสำคัญสูงสุดที่เธรดสามารถมีได้ */ สาธารณะสุดท้าย int max_priority = 10;
เราสามารถตั้งค่าลำดับความสำคัญของเธรดผ่าน setPriority และสามารถส่งผ่านตัวแปรคงที่สามตัวสำหรับการอุทธรณ์โดยตรงหรือส่งผ่านหมายเลข 1-10 โดยตรง หลังจากตั้งค่าเธรดจะมีลำดับความสำคัญที่แตกต่างกัน จะเกิดอะไรขึ้นถ้าเราไม่ได้ตั้งความสำคัญ?
ลำดับความสำคัญของเธรดได้รับการสืบทอด ถ้าเราเริ่มเธรด B ในเธรด A, AB มีลำดับความสำคัญเท่ากัน โดยทั่วไปเมื่อเราเริ่มเธรดในเธรดหลักเรามีลำดับความสำคัญที่สอดคล้องกับเธรดหลัก ลำดับความสำคัญของเธรดหลักคือ 5 โดยค่าเริ่มต้น
นี่คือกฎบางอย่างสำหรับความสำคัญ:
ผู้พิทักษ์ด้าย
JDK จัดเตรียมเมธอด setdaemon เพื่อตั้งค่าเธรดให้กลายเป็นเธรด daemon ลักษณะของเธรด daemon คือหลังจากที่มีการดำเนินการเธรดที่ไม่ใช่วันเดียวอื่น ๆ แล้วเธรด daemon จะถูกทำลายโดยอัตโนมัติ ตัวอย่างทั่วไปคือ GC Recycler
สำหรับรายละเอียดคุณสามารถเห็นการตรวจสอบ emonthread และ exampledaemonthreadtest
สรุป
บทความนี้ส่วนใหญ่สรุปการใช้งานพื้นฐานของกระทู้ Java และรวมอยู่ในบทความที่สองเกี่ยวกับความปลอดภัยของเธรดและการซิงโครไนซ์
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะช่วยในการศึกษาหรือทำงานของทุกคน ฉันหวังว่าจะสนับสนุน Wulin.com เพิ่มเติม!