โดยทั่วไปเมื่อเราเรียกใช้วิธีการในคลาสอื่น ๆ ใน Java ไม่ว่าจะเป็นการโทรแบบคงที่หรือแบบไดนามิกพวกเขาจะถูกดำเนินการในกระบวนการปัจจุบันนั่นคือมีอินสแตนซ์ของเครื่องเสมือน Java เพียงตัวเดียวเท่านั้น บางครั้งเราต้องเริ่มต้นกระบวนการย่อย Java หลายรายการผ่านรหัส Java แม้ว่าการทำเช่นนี้จะใช้ทรัพยากรระบบบางอย่าง แต่จะทำให้โปรแกรมมีความเสถียรมากขึ้นเนื่องจากโปรแกรมที่เริ่มต้นใหม่ทำงานในกระบวนการเสมือนจริงที่แตกต่างกัน
ใน Java เราสามารถใช้สองวิธีเพื่อให้บรรลุข้อกำหนดนี้ วิธีที่ง่ายที่สุดคือการเรียกใช้ Java classname ผ่านวิธี EXEC ในรันไทม์ หากการดำเนินการสำเร็จวิธีนี้จะส่งคืนวัตถุกระบวนการ ลองมาดูตัวอย่างง่ายๆด้านล่าง
// test1.java นำเข้าไฟล์ java.io.*; การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {fileOutputStream fout = ใหม่ fileOutputStream ("c: // test1 .txt"); fout.close () ; System.out.println ("เรียกสำเร็จ!");}} // test_exec.javapublic คลาส test_exec {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {runtime run = runtime.ge Truntime (); กระบวนการ p = run exec ("java test1");}}หลังจากเรียกใช้โปรแกรมผ่าน java test_exec ฉันพบว่ามีไฟล์ test1.txt เพิ่มเติมบนไดรฟ์ C แต่ข้อมูลผลลัพธ์ "เรียกว่าสำเร็จ!" ไม่ปรากฏในคอนโซล ดังนั้นจึงสามารถสรุปได้ว่าการทดสอบได้ดำเนินการสำเร็จแล้ว แต่ด้วยเหตุผลบางอย่างข้อมูลเอาต์พุตของการทดสอบไม่ได้ส่งออกในคอนโซลของ test_exec เหตุผลนี้ก็ง่ายมากเนื่องจากกระบวนการเด็กของ test_exec ถูกสร้างขึ้นโดยใช้ EXEC
หากคุณต้องการเอาต์พุตข้อมูลเอาต์พุตของกระบวนการเด็กคุณสามารถรับกระแสเอาต์พุตของกระบวนการเด็กผ่าน GetInputStream ในกระบวนการ (เอาต์พุตในกระบวนการเด็กอินพุตในกระบวนการหลัก) จากนั้นถ่ายโอนกระแสเอาต์พุตของเด็ก กระบวนการจากเอาต์พุตคอนโซลของกระบวนการหลัก รหัสการใช้งานเฉพาะมีดังนี้:
// test_exec_out.javaimport java.io.*; คลาสสาธารณะ test_exec_out {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {runtime run = runtime.getRuntime (); กระบวนการ p = run.exec ("Java test1"); bufferedInputStream = ใหม่ bufferedInputStream (P.GetInputStream ()); bufferedReader br = new bufferedReader (ใหม่ inputStreamReader (in)); String s; ในขณะที่ (s = br.readline ())! = null) system.out.println (s) ;}}
ดังที่เห็นได้จากรหัสข้างต้นใน test_exec_out.java ข้อมูลเอาต์พุตของกระบวนการเด็กจะถูกอ่านตามแถวจากนั้นเอาต์พุตจะดำเนินการในแต่ละบรรทัดใน test_exec_out การสนทนาข้างต้นคือวิธีรับข้อมูลผลลัพธ์ของกระบวนการเด็ก จากนั้นนอกเหนือจากข้อมูลเอาต์พุตแล้วยังมีข้อมูลอินพุต เนื่องจากกระบวนการเด็กไม่มีคอนโซลของตัวเองข้อมูลอินพุตจะต้องจัดทำโดยกระบวนการหลักด้วย เราสามารถให้ข้อมูลอินพุตกับกระบวนการเด็กผ่านวิธีการของ GetOutputStream ของกระบวนการ (นั่นคือข้อมูลอินพุตจากกระบวนการพาเรนต์ไปยังกระบวนการเด็กแทนที่จะเป็นข้อมูลอินพุตจากคอนโซล) เราสามารถดูรหัสต่อไปนี้:
// test2.java นำเข้าไฟล์ java.io.*; การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {bufferedreader br = bufferedreader ใหม่ (ใหม่ inputstreamread er (system.in)); system.out.println ( "ข้อมูลที่ป้อนโดยกระบวนการหลัก:" + br.readline ());}} // test_exec_in.javaimport java.io.*; คลาสสาธารณะ test_exec_in {โมฆะสาธารณะคง .getRuntime (); กระบวนการ p = run.exec ("java test2"); bufferedWriter bw = bufferedWriter ใหม่ (ใหม่ outputStreamWriter (p.getOutputStream ())); bw.write ("เอาต์พุตไปยังข้อมูลกระบวนการเด็ก"); BW flush (); bw.close (); จากรหัสข้างต้นเราจะเห็นว่า test1 ได้รับข้อมูลที่ส่งโดย test_exec_in และส่งออก เมื่อคุณไม่เพิ่ม bw.flash () และ bw.close () ข้อมูลจะไม่ถึงกระบวนการเด็กซึ่งหมายความว่ากระบวนการเด็กจะเข้าสู่สถานะการปิดกั้น แต่เนื่องจากกระบวนการแม่ออกไป . หากคุณต้องการพิสูจน์สิ่งนี้คุณสามารถเพิ่ม System.in.read () ในตอนท้ายจากนั้นดูกระบวนการ Java ผ่านตัวจัดการงาน (ใต้ Windows) และคุณจะพบว่าหากคุณเพิ่ม bw.flush () และ bw .close () มีกระบวนการ Java เพียงอันเดียวเท่านั้นหากถูกลบออกจะมีกระบวนการ Java สองกระบวนการอยู่ นี่เป็นเพราะหากข้อมูลถูกส่งไปยัง Test2, Test2 ออกหลังจากได้รับข้อมูล นี่คือสิ่งหนึ่งที่ต้องอธิบายว่าการดำเนินการของ EXEC เป็นแบบอะซิงโครนัสและจะไม่หยุดการเรียกใช้รหัสต่อไปนี้เนื่องจากโปรแกรมบางอย่างที่ดำเนินการถูกบล็อก ดังนั้นหลังจากเรียกใช้ test2 รหัสต่อไปนี้ยังสามารถดำเนินการได้
วิธีการบริหารได้รับการโหลดซ้ำหลายครั้ง สิ่งที่ใช้ด้านบนเป็นเพียงการโอเวอร์โหลดของมัน นอกจากนี้ยังสามารถแยกคำสั่งและพารามิเตอร์เช่น exec ("java.test2") สามารถเขียนเป็น exec ("java", "test2") EXEC ยังสามารถเรียกใช้เครื่องเสมือน Java ที่มีการกำหนดค่าที่แตกต่างกันผ่านตัวแปรสภาพแวดล้อมที่ระบุ
นอกเหนือจากการใช้วิธีการบริหารของ Runtime เพื่อสร้างกระบวนการเด็กแล้วคุณยังสามารถสร้างกระบวนการเด็กผ่าน ProcessBuilder การใช้ ProcessBuilder มีดังนี้:
// test_exec_out.javaimport java.io.*; คลาสสาธารณะ test_exec_out {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {processbuilder pb = processbui lder ใหม่ ("java", "test1"); กระบวนการ p = pb.start () ;ในการสร้างกระบวนการเด็ก ProcessBuilder นั้นคล้ายกับรันไทม์ หลังจากได้รับกระบวนการแล้วการดำเนินงานของพวกเขาจะเหมือนกันทุกประการ
เช่นเดียวกับรันไทม์ ProcessBuilder ยังสามารถตั้งค่าข้อมูลสภาพแวดล้อมไดเรกทอรีการทำงาน ฯลฯ ของไฟล์ปฏิบัติการ ตัวอย่างต่อไปนี้อธิบายวิธีการตั้งค่าข้อมูลนี้โดยใช้ ProcessBuilder
ProcessBuilder pb = processbuilder ใหม่ ("คำสั่ง", "arg2", "arg2", '' '); // ตั้งค่าตัวแปรสภาพแวดล้อม MAP <String, string> env = pb.environment (); env.put ("key1" ::::::::::::::::::::::: กระทาน ::::::::::::::::::::::: กระทาน :::::::::::::::::::::::::::::::::::::::: กระทาน. .get ("key1") + "_test"); pb.directory ("../ abcd"); ปัญหาการปิดกั้นปัญหา
กระบวนการที่แสดงโดยกระบวนการบางครั้งไม่ทำงานได้ดีในบางแพลตฟอร์มโดยเฉพาะอย่างยิ่งเมื่อทำงานกับสตรีมอินพุตมาตรฐานสตรีมเอาท์พุทและเอาต์พุตข้อผิดพลาดที่แสดงถึงกระบวนการ
หากคำสั่งในตัวอย่างข้างต้นที่อ่านข้อมูลจากเอาต์พุตมาตรฐานได้รับการแก้ไขเพื่ออ่านจากสตรีมเอาต์พุตข้อผิดพลาด:
stdout = ใหม่ bufferedReader (ใหม่ inputStreamReader (P.GetERRORSTREAM ()));
จากนั้นโปรแกรมจะบล็อกและไม่สามารถดำเนินการได้ แต่แขวนอยู่ที่นั่น
เมื่อกระบวนการเริ่มต้นกระแสเอาต์พุตมาตรฐานและสตรีมเอาต์พุตข้อผิดพลาดจะเปิดขึ้นเพื่อเตรียมเอาต์พุตและเมื่อกระบวนการสิ้นสุดลง ในตัวอย่างข้างต้นสตรีมเอาต์พุตข้อผิดพลาดไม่มีข้อมูลที่จะส่งออกและสตรีมเอาต์พุตมาตรฐานมีเอาต์พุตข้อมูล เนื่องจากข้อมูลในสตรีมเอาต์พุตมาตรฐานไม่ได้อ่านกระบวนการจะไม่สิ้นสุดและสตรีมเอาต์พุตข้อผิดพลาดจะไม่ถูกปิด ในการแก้ปัญหานี้คุณสามารถอ่านสตรีมเอาต์พุตมาตรฐานก่อนจากนั้นอ่านกระแสเอาต์พุตที่ไม่ถูกต้องตามลำดับจริงของเอาต์พุต
อย่างไรก็ตามในหลายกรณีลำดับผลลัพธ์ไม่สามารถทราบได้อย่างชัดเจนโดยเฉพาะอย่างยิ่งเมื่อต้องการอินพุตมาตรฐานสถานการณ์จะซับซ้อนมากขึ้น ในเวลานี้เธรดสามารถใช้ในการประมวลผลเอาต์พุตมาตรฐานเอาต์พุตข้อผิดพลาดและอินพุตมาตรฐานแยกกันและสตรีมหรือข้อมูลสามารถอ่านได้ตามความสัมพันธ์เชิงตรรกะทางธุรกิจของพวกเขา
สำหรับปัญหาที่เกิดจากสตรีมเอาท์พุทมาตรฐานและสตรีมเอาต์พุตที่ผิดพลาดคุณสามารถใช้วิธีการ RedirecterRorStream () ของ ProcessBuilder เพื่อรวมเข้าด้วยกัน
เมื่อใช้วิธีการรอ () ของกระบวนการในโปรแกรมโดยเฉพาะอย่างยิ่งเมื่อเรียกวิธีการรอ () ก่อนที่จะอ่านมันอาจทำให้เกิดการอุดตัน คุณสามารถใช้วิธีเธรดเพื่อแก้ปัญหานี้หรือคุณสามารถเรียกวิธีการรอ () หลังจากอ่านข้อมูลเพื่อรอให้โปรแกรมสิ้นสุดลง
ในระยะสั้นที่นี่ฉันแนะนำการใช้คลาส ProcessBuilder โดยใช้วิธีการ RedirecterRorStream เพื่อรวมสตรีมเอาต์พุตมาตรฐานและสตรีมเอาต์พุตข้อผิดพลาดเป็นหนึ่งเดียว จากนั้นโทรหาวิธีการรอ () เพื่อรอให้กระบวนการสิ้นสุด
ชอบ:
นำเข้า Java.io.BufferedReader; นำเข้า java.io.file; {ลอง {list <string> list = new ArrayList <String> (); เพิ่ม ("cmd.exe"); ); (line = stdout.readline ())! = null) {system.out.println (บรรทัด); stdou t .close ();