ก่อนอื่นมาพูดคุยเกี่ยวกับความรู้เชิงแนวคิดที่เกี่ยวข้องกับแอปพลิเคชันและกระบวนการใน Java จากนั้นอธิบายวิธีการสร้างเธรดและวิธีการสร้างกระบวนการ นี่คือโครงร่างไดเรกทอรีของบทความนี้:
1. กำหนดที่เกี่ยวข้องกับแอปพลิเคชันและกระบวนการใน Java
2. วิธีการสร้างเธรดใน Java
3. สร้างกระบวนการใน Java ได้อย่างไร
1. กำหนดที่เกี่ยวข้องกับแอปพลิเคชันและกระบวนการใน Java
ใน Java แอปพลิเคชันสอดคล้องกับอินสแตนซ์ JVM (เรียกอีกอย่างว่ากระบวนการ JVM) และชื่อเริ่มต้นเป็น java.exe หรือ javaw.exe (สามารถดูได้ผ่านตัวจัดการงานภายใต้ Windows) Java ใช้โมเดลการเขียนโปรแกรมแบบเธรดเดี่ยวนั่นคือถ้าเราไม่ได้สร้างเธรดในโปรแกรมของเราเองเราจะสร้างเธรดเดียวซึ่งมักจะเรียกว่าเธรดหลัก แต่โปรดทราบว่าแม้ว่าจะมีเธรดเพียงหนึ่งเธรดที่ทำงานไม่ได้หมายความว่ามีเพียงหนึ่งเธรดใน JVM เมื่อสร้างอินสแตนซ์ JVM จะมีการสร้างเธรดอื่น ๆ อีกมากมาย (เช่น Garbage Collector Threads)
เนื่องจาก Java ใช้โมเดลการเขียนโปรแกรมแบบเธรดเดี่ยวเมื่อการเขียนโปรแกรม UI คุณควรให้ความสนใจกับการใช้เวลานานในเธรดเด็กเพื่อหลีกเลี่ยงการปิดกั้นเธรดหลัก (เมื่อการเขียนโปรแกรม UI เธรดหลักคือเธรด UI ซึ่งใช้เพื่อจัดการเหตุการณ์การโต้ตอบของผู้ใช้)
2. วิธีการสร้างเธรดใน Java
หากคุณต้องการสร้างเธรดใน Java โดยทั่วไปมีสองวิธี: 1) สืบทอดคลาสเธรด 2) ใช้อินเทอร์เฟซ Runnable
1. สืบทอดคลาสเธรด
หากคุณสืบทอดคลาสเธรดคุณต้องแทนที่วิธีการเรียกใช้และกำหนดงานที่ต้องดำเนินการในวิธีการเรียกใช้
คลาส MyThread ขยายเธรด {ส่วนตัวคงที่ int num = 0; Public Mythread () {num ++; } @Override โมฆะสาธารณะเรียกใช้ () {system.out.println ("สร้างขึ้นอย่างแข็งขัน"+num+"เธรด"); -หลังจากสร้างคลาสเธรดของคุณเองคุณสามารถสร้างวัตถุเธรดแล้วเริ่มเธรดผ่านวิธีการเริ่มต้น () โปรดทราบว่ามันไม่ได้เรียกว่าวิธีการเรียกใช้ () เพื่อเริ่มเธรด วิธีการเรียกใช้จะกำหนดเฉพาะงานที่ต้องดำเนินการ หากวิธีการเรียกใช้เรียกว่ามันจะเทียบเท่ากับการดำเนินการวิธีการเรียกใช้ในเธรดหลักซึ่งไม่แตกต่างจากการเรียกวิธีการทั่วไป ในเวลานี้เธรดใหม่จะไม่ถูกสร้างขึ้นเพื่อดำเนินการงานที่กำหนด
การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {เธรด MyThread = ใหม่ MyThread (); thread.start (); }} คลาส MyThread ขยายเธรด {ส่วนตัวคงที่ int num = 0; Public Mythread () {num ++; } @Override โมฆะสาธารณะเรียกใช้ () {system.out.println ("สร้างขึ้นอย่างแข็งขัน"+num+"เธรด"); -ในรหัสด้านบนโดยเรียกเมธอด start () เธรดใหม่จะถูกสร้างขึ้น เพื่อแยกแยะความแตกต่างระหว่างการเรียกใช้เมธอดเริ่มต้น () การเรียกใช้วิธีการเรียกใช้ () โปรดดูตัวอย่างต่อไปนี้:
การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println ("รหัสเธรดหลัก:"+thread.currentthread (). getId ()); MyThread Thread1 = New MyThread ("Thread1"); Thread1.start (); MyThread Thread2 = New MyThread ("Thread2"); Thread2.run (); }} คลาส MyThread ขยายเธรด {ชื่อสตริงส่วนตัว; Public Mythread (ชื่อสตริง) {this.name = name; } @Override โมฆะสาธารณะเรียกใช้ () {system.out.println ("ชื่อ:"+ชื่อ+"รหัสเธรดลูก:"+thread.currentthread (). getId ()); -ผลการทำงาน:
จากผลลัพธ์ผลลัพธ์ข้อสรุปต่อไปนี้สามารถวาดได้:
1) ID เธรดของ Thread1 และ Thread2 นั้นแตกต่างกันและ Thread2 และ ID เธรดหลักจะเหมือนกันซึ่งหมายความว่าการเรียกใช้วิธีการเรียกใช้จะไม่สร้างเธรดใหม่ แต่จะเรียกใช้วิธีการเรียกใช้โดยตรงในเธรดหลักซึ่งไม่แตกต่างจากการเรียกวิธีปกติ
2) แม้ว่าวิธีการเริ่มต้นการเรียกของ Thread1 จะถูกเรียกก่อนวิธีการเรียกใช้ของ Thread2 แต่ข้อมูลเกี่ยวกับการเรียกใช้เมธอดการเรียกใช้ของ Thread2 นั้นเป็นเอาต์พุตก่อนแสดงว่ากระบวนการสร้างเธรดใหม่จะไม่บล็อกการดำเนินการที่ตามมาของเธรดหลัก
2. ใช้อินเทอร์เฟซ Runnable
นอกเหนือจากการสืบทอดคลาสเธรดการสร้างเธรดใน Java ยังสามารถใช้ฟังก์ชั่นที่คล้ายกันโดยใช้อินเตอร์เฟส Runnable การใช้อินเทอร์เฟซที่รันได้จะต้องแทนที่วิธีการเรียกใช้
นี่คือตัวอย่าง:
การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println ("รหัสเธรดหลัก:"+thread.currentthread (). getId ()); myrunnable runnable = new myrunnable (); เธรดเธรด = เธรดใหม่ (Runnable); thread.start (); }} คลาส myrunnable onplunable runnable {public myrunnable () {} @Override โมฆะสาธารณะเรียกใช้ () {system.out.println ("Subthread ID:"+thread.currentthread (). getId ()); -Runnable หมายถึง "งาน" เป็นภาษาจีน ตามชื่อแนะนำโดยการใช้อินเตอร์เฟส Runnable เราจะกำหนดภารกิจย่อยแล้วส่งไปที่ Subtask เพื่อดำเนินการเพื่อดำเนินการ โปรดทราบว่าวิธีนี้จะต้องใช้ Runnable เป็นพารามิเตอร์ไปยังคลาสเธรดจากนั้นสร้างเธรดใหม่เพื่อเรียกใช้งาน subtask ผ่านวิธีการเริ่มต้นเธรด หากวิธีการเรียกใช้ของ Runnable ถูกเรียกว่าเธรดใหม่จะไม่ถูกสร้างขึ้นและไม่มีความแตกต่างระหว่างการเรียกวิธีปกตินี้
ในความเป็นจริงหากคุณดูที่ซอร์สโค้ดการใช้งานของคลาสเธรดคุณจะพบว่าคลาสเธรดใช้อินเทอร์เฟซ Runnable
ใน Java วิธีการทั้งสองนี้สามารถใช้ในการสร้างเธรดเพื่อดำเนินการงานย่อย วิธีการเฉพาะในการเลือกขึ้นอยู่กับความต้องการของคุณ หากคุณสืบทอดคลาสเธรดโดยตรงอาจดูกระชับมากกว่าการใช้อินเทอร์เฟซที่รันได้ อย่างไรก็ตามเนื่องจาก Java อนุญาตให้รับมรดกเดี่ยวเท่านั้นหากคลาสที่กำหนดเองจำเป็นต้องสืบทอดคลาสอื่น ๆ คุณสามารถเลือกที่จะใช้อินเตอร์เฟส Runnable เท่านั้น
3. สร้างกระบวนการใน Java ได้อย่างไร
ใน Java มีสองวิธีในการสร้างกระบวนการที่เกี่ยวข้องกับทั้งหมด 5 คลาสหลัก
วิธีแรกคือการสร้างกระบวนการผ่านวิธี runtime.exec () และวิธีที่สองคือการสร้างกระบวนการผ่านวิธีการเริ่มต้นของ ProcessBuilder มาพูดคุยเกี่ยวกับความแตกต่างและการเชื่อมต่อระหว่างสองวิธีนี้
สิ่งแรกที่ฉันอยากพูดถึงคือคลาสกระบวนการ คลาสกระบวนการเป็นคลาสนามธรรม ส่วนใหญ่มีวิธีนามธรรมหลายวิธีในนั้น คุณสามารถเรียนรู้สิ่งนี้ได้โดยดูที่ซอร์สโค้ดของคลาสกระบวนการ:
ตั้งอยู่ใน Java.lang.Process Path:
การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println ("รหัสเธรดหลัก:"+thread.currentthread (). getId ()); myrunnable runnable = new myrunnable (); เธรดเธรด = เธรดใหม่ (Runnable); thread.start (); }} คลาส myrunnable onplunable runnable {public myrunnable () {} @Override โมฆะสาธารณะเรียกใช้ () {system.out.println ("Subthread ID:"+thread.currentthread (). getId ()); -1) สร้างกระบวนการผ่าน ProcessBuilder
ProcessBuilder เป็นคลาสสุดท้ายที่มีสองตัวสร้าง:
ProcessBuilder คลาสสุดท้ายของคลาสสาธารณะ {รายการส่วนตัว <String> คำสั่ง; ไดเรกทอรีไฟล์ส่วนตัว แผนที่ส่วนตัว <สตริงสตริง> สภาพแวดล้อม; บูลีนส่วนตัว redirecterRorStream; ProcessBuilder สาธารณะ (รายการ <String> คำสั่ง) {ถ้า (command == null) โยน nullpointerexception ใหม่ (); this.command = คำสั่ง; } processBuilder (สตริง ... คำสั่ง) {this.command = new ArrayList <String> (command.length); สำหรับ (String arg: command) this.command.add (arg); -ตัวสร้างผ่านพารามิเตอร์คำสั่งของกระบวนการที่ต้องสร้าง ตัวสร้างแรกวางพารามิเตอร์คำสั่งลงในรายการและส่งผ่านในรูปแบบของสตริงที่มีความยาวอย่างไม่มีกำหนด
ดังนั้นมาดูกันต่อไป ดังที่ได้กล่าวไว้ก่อนหน้านี้เราสร้างกระบวนการใหม่ผ่านวิธีการเริ่มต้นของ ProcessBuilder ลองมาดูสิ่งที่ทำในวิธีการเริ่มต้น ต่อไปนี้เป็นซอร์สโค้ดการใช้งานเฉพาะของวิธีการเริ่มต้น:
กระบวนการสาธารณะเริ่มต้น () พ่น IOException {// ต้องแปลงเป็นอาร์เรย์ก่อน-ผู้ใช้ที่เป็นอันตราย // รายการอาจพยายามที่จะตรวจสอบความปลอดภัยการตรวจสอบความปลอดภัย [] cmdarray = command.toarray (สตริงใหม่ [command.size ()); istystring prog = cmdarray [0]; SecurityManager Security = System.getSecurityManager (); ถ้า (ความปลอดภัย! = null) security.checkexec (prog); String dir = directory == null? null: directory.toString (); ลอง {return processimpl.start (cmdarray, environment, dir, redirecterRorStream);} catch (ioexception e) {// มันง่ายกว่ามากสำหรับเราในการสร้างข้อผิดพลาดคุณภาพสูง // ข้อความมากกว่ารหัส C ระดับต่ำซึ่งพบปัญหา โยน iOexception ใหม่ ("ไม่สามารถเรียกใช้โปรแกรม /" " + prog +" /"" + (dir == null? "": "(ในไดเรกทอรี /" " + dir +" /")") + ":" + e.getMessage (), e);}}}วิธีนี้ส่งคืนวัตถุกระบวนการ ส่วนแรกของวิธีนี้เทียบเท่ากับการตั้งค่าพารามิเตอร์บางอย่างตามพารามิเตอร์คำสั่งและไดเรกทอรีการทำงานที่ตั้งไว้ สิ่งที่สำคัญที่สุดคือประโยคในบล็อกคำสั่งลอง:
return processimpl.start (cmdarray, environment, dir, redirecterRorStream);
นี่คือประโยคที่สร้างกระบวนการจริงๆ โปรดทราบว่าวิธีการเริ่มต้นของคลาส ProcessImpl เรียกว่า ที่นี่เราสามารถรู้ได้ว่าการเริ่มต้นจะต้องเป็นวิธีการคงที่ ProcessImpl คืออะไร? คลาสนี้ตั้งอยู่ในเส้นทาง java.lang.processimpl มาดูการใช้งานเฉพาะของคลาสนี้:
ProcessImpl ยังเป็นคลาสสุดท้ายซึ่งสืบทอดคลาสกระบวนการ:
ProcessImpl คลาสสุดท้ายขยายกระบวนการ {// ส่วนที่ขึ้นอยู่กับระบบของ processbuilder.start () กระบวนการเริ่มต้น (สตริง cmdarray [], java.util.map <สตริง, สตริง> สภาพแวดล้อม, สตริง dir, บูลีน ส่งคืน ProcessImpl ใหม่ (cmdarray, envblock, dir, redirecterRorStream); -นี่คือการใช้งานเฉพาะของวิธีการเริ่มต้นของคลาส ProcessImpl ในความเป็นจริงประโยคนี้ใช้เพื่อสร้างวัตถุ ProcessImpl:
ส่งคืน ProcessImpl ใหม่ (cmdarray, envblock, dir, redirecterRorStream);
ใน ProcessImpl วิธีการนามธรรมหลายวิธีในคลาสกระบวนการถูกนำไปใช้ในการใช้งานที่เป็นรูปธรรม
ในความเป็นจริงวัตถุ ProcessImpl ถูกสร้างขึ้นผ่านวิธีการเริ่มต้นของ ProcessBuilder
มาดูตัวอย่างของการใช้ ProcessBuilder เพื่อสร้างกระบวนการ ตัวอย่างเช่นหากฉันต้องการเริ่มกระบวนการผ่าน ProcessBuilder เพื่อเปิด CMD และรับข้อมูลที่อยู่ IP ฉันสามารถเขียนได้เช่นนี้:
การทดสอบระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่น IOException {processBuilder pb = processbuilder ใหม่ ("cmd", "/c", "ipconfig/all"); กระบวนการกระบวนการ = pb.start (); สแกนเนอร์สแกนเนอร์ = สแกนเนอร์ใหม่ (process.getInputStream ()); ในขณะที่ (scanner.hasnextline ()) {system.out.println (scanner.nextline ()); } scanner.close (); -ขั้นตอนแรกคือขั้นตอนที่สำคัญที่สุดซึ่งคือการส่งสตริงคำสั่งไปยังตัวสร้าง ProcessBuilder โดยทั่วไปการพูดแต่ละคำสั่งอิสระในสตริงจะใช้เป็นพารามิเตอร์แยกต่างหาก แต่ก็สามารถวางไว้ในรายการตามลำดับและส่งผ่าน
สำหรับการใช้งานเฉพาะอื่น ๆ อีกมากมายฉันจะไม่อธิบายรายละเอียดเกี่ยวกับพวกเขาที่นี่เช่นการตั้งค่าตัวแปรสภาพแวดล้อมกระบวนการและไดเรกทอรีการทำงานผ่านวิธีการสภาพแวดล้อมและไดเรกทอรีของ ProcessBuilder (ไดเรกทอรีไฟล์) เพื่อนที่สนใจสามารถดูเอกสาร API ที่เกี่ยวข้อง
2) สร้างกระบวนการผ่านวิธี EXEC ของ Runtime
ก่อนอื่นมาดูการใช้งานเฉพาะของคลาสรันไทม์คลาสและวิธีการ รันไทม์ตามชื่อแนะนำหมายถึงการรันแทนอินสแตนซ์ของเครื่องเสมือนซึ่งกระบวนการปัจจุบันอยู่
เนื่องจากกระบวนการใด ๆ จะทำงานในอินสแตนซ์ของเครื่องเสมือนเดียวเท่านั้นโหมด Singleton จึงใช้ในรันไทม์นั่นคืออินสแตนซ์ของเครื่องเสมือนเดียวเท่านั้นที่จะถูกสร้างขึ้น:
Runtime คลาสสาธารณะ {private static runtime currentruntime = new runtime (); /*** ส่งคืนวัตถุรันไทม์ที่เกี่ยวข้องกับแอปพลิเคชัน Java ปัจจุบัน * วิธีการส่วนใหญ่ของคลาส <code> รันไทม์ </code> เป็นเมธอดอินสแตนซ์ * และต้องเรียกใช้ตามวัตถุรันไทม์ปัจจุบัน * * @return วัตถุ <code> รันไทม์ </code> วัตถุที่เชื่อมโยงกับแอปพลิเคชัน * Java ปัจจุบัน */ รันไทม์คงที่สาธารณะ getRuntime () {ส่งคืนกระแสไฟฟ้า; } / ** อย่าปล่อยให้คนอื่นอินสแตนซ์คลาสนี้* / private runtime () {} ... }จากที่นี่เราจะเห็นได้ว่าเนื่องจากตัวสร้างของคลาสรันไทม์เป็นส่วนตัวเราสามารถรับอินสแตนซ์รันไทม์ผ่าน GetRuntime เท่านั้น จากนั้นมาดูการใช้วิธีการบริหารอย่างใกล้ชิด มีการใช้งานโอเวอร์โหลดที่แตกต่างกันหลายครั้งในรันไทม์ แต่การสิ้นสุดของการดำเนินการคือวิธีการ EXEC รุ่นนี้:
กระบวนการสาธารณะ EXEC (String [] CMDARRAY, String [] EnvP, File DIR) พ่น IOException {ส่งคืน ProcessBuilder ใหม่ (CMDARRAY). Environment (EnvP) .Directory (DIR) .start (); -จะพบได้ว่าในความเป็นจริงหากกระบวนการถูกสร้างขึ้นผ่าน EXEC ของคลาสรันไทม์มันจะถูกสร้างขึ้นในที่สุดผ่านวิธีการเริ่มต้นของคลาส ProcessBuilder
ลองมาดูตัวอย่างเพื่อดูวิธีการสร้างกระบวนการผ่าน EXEC ของ Runtime หรือตัวอย่างก่อนหน้านี้โทร CMD เพื่อรับข้อมูลที่อยู่ IP:
การทดสอบระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่น IOException {สตริง cmd = "cmd"+"/c"+"ipconfig/all"; กระบวนการกระบวนการ = runtime.getRuntime (). exec (cmd); สแกนเนอร์สแกนเนอร์ = สแกนเนอร์ใหม่ (process.getInputStream ()); ในขณะที่ (scanner.hasnextline ()) {system.out.println (scanner.nextline ()); } scanner.close (); -ควรสังเกตว่าเมธอด EXED ไม่รองรับพารามิเตอร์ที่ไม่ จำกัด (ProcessBuilder รองรับพารามิเตอร์ที่มีความยาวไม่ จำกัด ) ดังนั้นพารามิเตอร์คำสั่งจะต้องถูกเชื่อมต่อก่อนก่อนที่จะส่งผ่าน
ฉันจะพูดถึงวิธีการสร้างเธรดและกระบวนการใน Java ในขณะนี้ เพื่อนที่สนใจสามารถอ้างถึงข้อมูลที่เกี่ยวข้อง