CodeGym /จาวาบล็อก /สุ่ม /มัลติเธรดใน Java
John Squirrels
ระดับ
San Francisco

มัลติเธรดใน Java

เผยแพร่ในกลุ่ม
สวัสดี! ก่อนอื่น ขอแสดงความยินดี: คุณมาถึงหัวข้อของ Multithreading ใน Java แล้ว! นี่เป็นความสำเร็จที่จริงจัง คุณมาไกลแล้ว แต่เตรียมตัวให้พร้อม: นี่เป็นหนึ่งในหัวข้อที่ยากที่สุดในหลักสูตร และไม่ใช่ว่าเรากำลังใช้คลาสที่ซับซ้อนหรือเมธอดมากมายที่นี่ อันที่จริง เราจะใช้น้อยกว่า 20 ตัว คุณจะต้องเปลี่ยนวิธีคิดเล็กน้อย ก่อนหน้านี้ โปรแกรมของคุณถูกดำเนินการตามลำดับ โค้ดบางบรรทัดมาหลังโค้ดอื่น บางเมธอดมาหลังโค้ดอื่น และทุกอย่างก็ชัดเจน ขั้นแรก เราคำนวณบางสิ่ง จากนั้นแสดงผลบนคอนโซล และจากนั้นโปรแกรมก็จบลง เพื่อให้เข้าใจการทำงานแบบมัลติเธรด จะเป็นการดีกว่าหากคิดในแง่ของความขนาน เริ่มจากสิ่งที่ค่อนข้างง่าย: ) จินตนาการว่าครอบครัวของคุณกำลังจะย้ายจากบ้านหลังหนึ่งไปอีกหลังหนึ่ง การรวบรวมหนังสือทั้งหมดของคุณจะเป็นส่วนสำคัญของการย้าย คุณมีหนังสือสะสมมากมาย และคุณต้องใส่กล่อง ขณะนี้ คุณเป็นคนเดียวที่ว่าง แม่กำลังเตรียมอาหาร พี่ชายกำลังเก็บเสื้อผ้า ส่วนน้องสาวไปที่ร้าน ลำพังคุณก็จัดการได้ ไม่ช้าก็เร็วคุณจะทำงานให้เสร็จด้วยตัวเอง แต่จะใช้เวลามาก อย่างไรก็ตาม พี่สาวของคุณจะกลับมาจากร้านในอีก 20 นาที และเธอไม่มีอะไรจะทำอีกแล้ว เธอจึงสามารถเข้าร่วมกับคุณได้ งานไม่ได้เปลี่ยนแปลง: ใส่หนังสือลงในกล่อง แต่กำลังดำเนินการเร็วขึ้นสองเท่า ทำไม เพราะงานจะเกิดขึ้นควบคู่กันไป 'เธรด' สองอันที่แตกต่างกัน (คุณและน้องสาวของคุณ) กำลังทำงานเดียวกันพร้อมกัน และถ้าไม่มีอะไรเปลี่ยนแปลง จากนั้นจะมีความแตกต่างของเวลาอย่างมากเมื่อเทียบกับสถานการณ์ที่คุณทำทุกอย่างด้วยตัวเอง ถ้าพี่ชายทำงานเสร็จเร็ว เขาช่วยคุณได้ และทุกอย่างก็จะเร็วขึ้นไปอีก

แก้ไขปัญหาโดยมัลติเธรด

มัลติเธรดถูกประดิษฐ์ขึ้นเพื่อบรรลุวัตถุประสงค์สำคัญสองประการ:
  1. ทำหลายอย่างในเวลาเดียวกัน

    ในตัวอย่างข้างต้น ด้ายต่างๆ (สมาชิกในครอบครัว) ดำเนินการหลายอย่างพร้อมกัน: พวกเขาล้างจาน ไปที่ร้าน และเก็บข้าวของ

    เราสามารถเสนอตัวอย่างที่เกี่ยวข้องกับการเขียนโปรแกรมมากขึ้น สมมติว่าคุณมีโปรแกรมที่มีส่วนติดต่อผู้ใช้ เมื่อคุณคลิก 'ดำเนินการต่อ' ในโปรแกรม การคำนวณบางอย่างควรเกิดขึ้น และผู้ใช้ควรเห็นหน้าจอต่อไปนี้ หากดำเนินการเหล่านี้ตามลำดับ โปรแกรมจะหยุดทำงานหลังจากที่ผู้ใช้คลิกปุ่ม 'ดำเนินการต่อ' ผู้ใช้จะเห็นหน้าจอที่มีหน้าจอปุ่ม 'ดำเนินการต่อ' จนกว่าโปรแกรมจะทำการคำนวณภายในทั้งหมดและไปถึงส่วนที่มีการรีเฟรชอินเทอร์เฟซผู้ใช้

    ฉันเดาว่าเราจะรอสองสามนาที!

    มัลติเธรดใน Java: คืออะไร ประโยชน์ และข้อผิดพลาดทั่วไป - 3

    หรือเราอาจปรับปรุงโปรแกรมของเรา หรืออย่างที่โปรแกรมเมอร์พูดว่า 'ขนานกัน' มาทำการคำนวณของเราบนเธรดหนึ่งและวาดอินเทอร์เฟซผู้ใช้บนเธรดอื่น คอมพิวเตอร์ส่วนใหญ่มีทรัพยากรเพียงพอที่จะทำเช่นนี้ หากเราใช้เส้นทางนี้ โปรแกรมจะไม่ค้างและผู้ใช้จะย้ายไปมาระหว่างหน้าจอได้อย่างราบรื่นโดยไม่ต้องกังวลกับสิ่งที่เกิดขึ้นภายใน หนึ่งไม่รบกวนอื่น ๆ :)

  2. ทำการคำนวณได้รวดเร็วยิ่งขึ้น

    ทุกอย่างง่ายกว่ามากที่นี่ หากโปรเซสเซอร์ของเรามีหลายคอร์ และโปรเซสเซอร์ส่วนใหญ่ในปัจจุบันมี คอร์หลายตัวสามารถจัดการรายการงานของเราพร้อมกันได้ เห็นได้ชัดว่า หากเราต้องดำเนินการ 1,000 งานและแต่ละงานใช้เวลา 1 วินาที หนึ่งคอร์สามารถทำงานเสร็จภายใน 1,000 วินาที สองคอร์ใน 500 วินาที สามคอร์ใน 333 วินาที เป็นต้น

แต่อย่างที่คุณได้อ่านไปแล้วในบทเรียนนี้ ระบบในปัจจุบันนั้นฉลาดมาก และแม้แต่แกนประมวลผลเดียวก็สามารถบรรลุความขนานหรือค่อนข้างขนานเทียม โดยที่งานจะถูกดำเนินการสลับกัน มาเปลี่ยนลักษณะทั่วไปเป็นเฉพาะเจาะจง และทำความรู้จักกับคลาสที่สำคัญที่สุดในไลบรารีมัลติเธรดของ Java — java.lang.Thread พูดอย่างเคร่งครัด เธรด Java ถูกแสดงโดยอินสแตนซ์ของคลาสเธรด ซึ่งหมายความว่าในการสร้างและเรียกใช้ 10 เธรด คุณต้องมี 10 อินสแตนซ์ของคลาสนี้ ลองเขียนตัวอย่างที่ง่ายที่สุด:

public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
ในการสร้างและเรียก ใช้ เธรด เราจำเป็นต้องสร้างคลาส ทำให้มันสืบทอดjava.lang คลาส เธรดและแทนที่เมธอดrun () ข้อกำหนดสุดท้ายนั้นสำคัญมาก มันอยู่ใน เมธอด run()ที่เรากำหนดตรรกะสำหรับเธรดของเราเพื่อดำเนินการ ตอนนี้ ถ้าเราสร้างและรันอินสแตนซ์ของMyFirstThread เมธอด run ()จะแสดงบรรทัดที่มีชื่อ: เมธอด getName()จะแสดงชื่อ 'ระบบ' ของเธรด ซึ่งกำหนดโดยอัตโนมัติ แต่ทำไมเราถึงพูดอย่างไม่แน่นอน? มาสร้างมันขึ้นมากันเถอะ!

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
เอาต์พุตคอนโซล: I'm Thread! ฉันชื่อเธรด-2 ฉันชื่อเธรด! ฉันชื่อเธรด-1 ฉันชื่อเธรด! ฉันชื่อเธรด-0 ฉันชื่อเธรด! ฉันชื่อเธรด-3 ฉันชื่อเธรด! ฉันชื่อเธรด-6 ฉันชื่อเธรด! ฉันชื่อเธรด-7 ฉันชื่อเธรด! ฉันชื่อเธรด-4 ฉันชื่อเธรด! ฉันชื่อเธรด-5 ฉันชื่อเธรด! ฉันชื่อเธรด-9 ฉันชื่อเธรด! ชื่อของฉันคือ Thread-8 มาสร้างเธรด 10 เธรด ( วัตถุ MyFirstThreadซึ่งสืบทอดThread ) และเริ่มต้นโดยการเรียกเมธอดstart()ในแต่ละวัตถุ หลังจากเรียกใช้เมธอดstart()ตรรกะในเมธอดrun()จะถูกดำเนินการ หมายเหตุ:ชื่อเธรดไม่เรียงตามลำดับ เป็นเรื่องแปลกที่พวกเขาไม่ได้เรียงตามลำดับ:, เธรด-1 , เธรด-2และอื่นๆ? สิ่งที่เกิดขึ้นนี้เป็นตัวอย่างหนึ่งของเวลาที่การคิดแบบ 'ตามลำดับ' ไม่เข้าท่า ปัญหาคือเราได้ให้คำสั่งในการสร้างและเรียกใช้ 10 เธรดเท่านั้น ตัวกำหนดตารางเวลาของเธรดซึ่งเป็นกลไกของระบบปฏิบัติการพิเศษ ตัดสินใจสั่งการดำเนินการ การออกแบบที่แม่นยำและกลยุทธ์การตัดสินใจเป็นหัวข้อสำหรับการสนทนาเชิงลึกที่เราจะไม่ลงลึกในตอนนี้ สิ่งสำคัญที่ต้องจำไว้คือโปรแกรมเมอร์ไม่สามารถควบคุมลำดับการดำเนินการของเธรดได้ เพื่อให้เข้าใจถึงความรุนแรงของสถานการณ์ ให้ลองเรียกใช้เมธอด main() ในตัวอย่างข้างต้นอีกสองสามครั้ง เอาต์พุตคอนโซลในการเรียกใช้ครั้งที่สอง: ฉันด้าย! ฉันชื่อเธรด-0 ฉันชื่อเธรด! ฉันชื่อเธรด-4 ฉันชื่อเธรด! ฉันชื่อเธรด-3 ฉันชื่อเธรด! ฉันชื่อเธรด-2 ฉันชื่อเธรด! ฉันชื่อเธรด-1 ฉันชื่อเธรด! ฉันชื่อเธรด-5 ฉันชื่อเธรด! ฉันชื่อเธรด-6 ฉันชื่อเธรด! ฉันชื่อเธรด-8 ฉันชื่อเธรด! ฉันชื่อเธรด-9 ฉันชื่อเธรด! ชื่อของฉันคือ Thread-7 Console เอาต์พุตจากการเรียกใช้ครั้งที่สาม: I'm Thread! ฉันชื่อเธรด-0 ฉันชื่อเธรด! ฉันชื่อเธรด-3 ฉันชื่อเธรด! ฉันชื่อเธรด-1 ฉันชื่อเธรด! ฉันชื่อเธรด-2 ฉันชื่อเธรด! ฉันชื่อเธรด-6 ฉันชื่อเธรด! ฉันชื่อเธรด-4 ฉันชื่อเธรด! ฉันชื่อเธรด-9 ฉันชื่อเธรด! ฉันชื่อเธรด-5 ฉันชื่อเธรด! ฉันชื่อเธรด-7 ฉันชื่อเธรด! ฉันชื่อเธรด-8

ปัญหาที่เกิดจากมัลติเธรด

ในตัวอย่างของเรากับหนังสือ คุณเห็นว่าการทำงานแบบมัลติเธรดช่วยแก้ปัญหาที่สำคัญมากและทำให้โปรแกรมของเราเร็วขึ้นได้ มักจะเร็วกว่าหลายเท่า แต่มัลติเธรดถือเป็นหัวข้อที่ยาก แท้จริงแล้วหากใช้ไม่ถูกต้องจะสร้างปัญหาแทนที่จะแก้ไข เมื่อฉันพูดว่า 'สร้างปัญหา' ฉันไม่ได้หมายความในแง่ที่เป็นนามธรรม มีปัญหาเฉพาะสองประการที่มัลติเธรดสามารถสร้างได้: การหยุดชะงักและสภาวะการแย่งชิง การหยุดชะงักคือสถานการณ์ที่หลายเธรดกำลังรอทรัพยากรที่แต่ละอื่น ๆ ถือครองอยู่ และไม่มีเธรดใดที่สามารถรันต่อไปได้ เราจะพูดถึงเรื่องนี้มากขึ้นในบทเรียนต่อๆ ไป ตัวอย่างต่อไปนี้จะเพียงพอสำหรับตอนนี้: มัลติเธรดใน Java: คืออะไร ประโยชน์ และข้อผิดพลาดทั่วไป - 4ลองนึกภาพว่า Thread-1 โต้ตอบกับ Object-1 บางตัว และ Thread-2 โต้ตอบกับ Object-2 นอกจากนี้ โปรแกรมยังเขียนขึ้นเพื่อให้:
  1. Thread-1 หยุดโต้ตอบกับ Object-1 และสลับไปยัง Object-2 ทันทีที่ Thread-2 หยุดโต้ตอบกับ Object-2 และสลับไปยัง Object-1
  2. Thread-2 หยุดโต้ตอบกับ Object-2 และสลับไปยัง Object-1 ทันทีที่ Thread-1 หยุดโต้ตอบกับ Object-1 และสลับไปยัง Object-2
แม้จะไม่มีความเข้าใจอย่างลึกซึ้งเกี่ยวกับมัลติเธรด คุณก็สามารถเห็นได้ง่ายว่าจะไม่มีอะไรเกิดขึ้น สายใยจะไม่สลับที่และจะรอกันตลอดไป ข้อผิดพลาดดูเหมือนชัดเจน แต่ในความเป็นจริงไม่ใช่ คุณสามารถทำได้ง่ายๆ ในโปรแกรม เราจะพิจารณาตัวอย่างโค้ดที่ทำให้หยุดชะงักในบทเรียนต่อๆ ไป อย่างไรก็ตาม Quora มีตัวอย่างในชีวิตจริงที่ยอดเยี่ยมซึ่งอธิบายว่าการหยุดชะงัก คืออะไรเป็น. 'ในบางรัฐของอินเดีย พวกเขาจะไม่ขายที่ดินเพื่อเกษตรกรรมให้คุณเว้นแต่คุณจะเป็นเกษตรกรที่ขึ้นทะเบียน อย่างไรก็ตาม พวกเขาจะไม่ลงทะเบียนคุณเป็นเกษตรกรหากคุณไม่ได้เป็นเจ้าของที่ดินเพื่อเกษตรกรรม' ยอดเยี่ยม! เราจะพูดอะไรดี?! :) ตอนนี้เรามาพูดถึงสภาพการแข่งขัน สภาวะการแย่งชิงคือข้อผิดพลาดในการออกแบบในระบบหรือแอปพลิเคชันแบบมัลติเธรด ซึ่งการทำงานของระบบหรือแอปพลิเคชันขึ้นอยู่กับลำดับในการประมวลผลส่วนใดของโค้ด จำไว้ ตัวอย่างที่เราตั้งกระทู้:

public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Thread executed: " + getName());
   }
}

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
ตอนนี้ลองนึกภาพว่าโปรแกรมมีหน้าที่ควบคุมหุ่นยนต์ที่ทำอาหาร! Thread-0 นำไข่ออกจากตู้เย็น หัวข้อ 1 เปิดเตา Thread-2 รับกระทะและวางบนเตา หัวข้อที่ 3 จุดเตา หัวข้อ 4 เทน้ำมันลงในกระทะ กระทู้ที่ 5 แบ่งไข่และเทลงในกระทะ Thread-6 โยนเปลือกไข่ลงถังขยะ Thread-7 นำไข่ที่สุกแล้วออกจากเตา Thread-8 วางไข่ที่ปรุงแล้วลงบนจาน กระทู้-9 ล้างจาน. ดูผลลัพธ์ของโปรแกรมของเรา: เธรดที่ดำเนินการ: เธรด-0 เธรดที่ดำเนินการ: เธรด-2 เธรดที่ดำเนินการ เธรด-1 เธรดที่ดำเนินการ: เธรด-4 เธรดที่ดำเนินการ: เธรด-9 เธรดที่ดำเนินการ: เธรด-5 เธรดที่ดำเนินการ: เธรด-8 เธรด ดำเนินการ: เธรด-7 เธรดที่ดำเนินการ: เธรด-3 นี่เป็นกิจวัตรตลกหรือไม่? :) และทั้งหมดเป็นเพราะการทำงานของโปรแกรมของเราขึ้นอยู่กับลำดับการดำเนินการของเธรด ห้องครัวของเรากลายเป็นนรกและหุ่นยนต์ที่บ้าคลั่งทำลายทุกสิ่งรอบตัวด้วยการละเมิดลำดับที่จำเป็นเพียงเล็กน้อย นี่เป็นปัญหาทั่วไปในการเขียนโปรแกรมแบบมัลติเธรด คุณจะได้ยินเกี่ยวกับเรื่องนี้มากกว่าหนึ่งครั้ง ในการสรุปบทเรียนนี้ ฉันขอแนะนำหนังสือเกี่ยวกับมัลติเธรด มัลติเธรดใน Java: คืออะไร ประโยชน์ และข้อผิดพลาดทั่วไป - 6'Java Concurrency in Practice' เขียนขึ้นในปี 2549 แต่ยังไม่สูญเสียความเกี่ยวข้องไป มีไว้สำหรับการเขียนโปรแกรม Java แบบมัลติเธรดโดยเฉพาะ ตั้งแต่พื้นฐานไปจนถึงข้อผิดพลาดและรูปแบบต่อต้านที่พบบ่อยที่สุด หากสักวันหนึ่งคุณตัดสินใจที่จะเป็นกูรูด้านมัลติเธรด หนังสือเล่มนี้เป็นสิ่งที่ต้องอ่าน แล้วพบกันในบทเรียนต่อไป! :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION