เกี่ยวกับ Java Concurrency พร้อมตัวอย่าง

07-มิ.ย.-19

คัมภีร์เทพ IT

บางครั้งเมื่อเขียน Java Application มันอาจจำเป็นที่ต้องมีหลาย Task ที่ทำงานพร้อม ๆ กัน ในกรณีเช่นนี้ การรู้วิธี Execute Task ในอีก Thread ที่แยกออกมาต่างหาก ถือเป็นสิ่งที่มีประโยชน์มาก ดังนั้นเรามาทำความรู้จักกับ Java Concurrency พร้อมตัวอย่างการใช้งานกัน

เมื่อเราต้องการ Run Code ใน Thread ที่แยกออกมาต่างหาก ใน Java เราอาจเขียน Code ดังเช่นตัวอย่างด้านล่างนี้:

(โปรดทราบว่า ในบทความนี้ เราจะสมมติว่า เรากำลัง Execute Task โดยที่ไม่มี Resource ที่ใช้ร่วมกัน)

ในตัวอย่างข้างต้น คุณจะสังเกตเห็นว่า เรากำลังสร้าง RunnableExample และกำลังใช้ Instance เป็น Parameter ใน  Thread Constructor การที่เราสามารถทำเช่นนี้ได้ ก็เพราะ RunnableExample เป็น Class ที่สามารถ Implement Runnable Interface ได้ ซึ่งเราสามารถ Override “abstract method run”

ด้านล่างนี้ คือ ตัวอย่างของการใช้ Class RunnableExample:

เมื่อเราเรียกใช้ Start Method บน Thread instance ของเรา run method ของ Runnable จะถูกเรียกใช้ และถูก Execute ใน Thread ที่แยกออกมาต่างหาก หากเราใช้ Java 8 เราสามารถย่อ Code นี้ให้สั้นลงได้ และหลีกเลี่ยงการสร้าง Class ที่ implements Runnable โดยใช้ Lambda Expression ตามตัวอย่างด้านล่าง:

เราสามารถใช้ Lambda Expression ได้ในตัวอย่างด้านบน เพราะ Runnable เป็น Functional Interface

ตัวอย่างของ Thread ด้านบนนั้นสามารถจัดการได้ง่าย แต่จะเกิดอะไรขึ้น ถ้าเรามี Task จำนวนมากที่จะต้อง Execute ใน Thread ที่แยกออกมาต่างหาก สิ่งนี้จะทำให้เราต้อง Maintain คิวของ Runnable รวมทั้ง Assign และเริ่มทำแต่ละ Process เมื่อ Thread พร้อมใช้งาน ซึ่งดูเหมือนว่าเราจะต้องใช้ Overhead ที่ค่อนข้างมาก ในการเขียนให้เป็นแบบที่เราต้องการ ด้วยสถานการณ์เช่นนี้ เราจึงหันมาใช้ExecutorService ซึ่ง ExecutorService สามารถใช้เพื่อเขียน Concurrent Application โดยไม่จำเป็นต้องจัดการกับ Thread ด้วยตัวเราเอง ลองดูตัวอย่างนี้:

โปรดสังเกตว่า เราจะไม่เรียก new เมื่อสร้าง ExecutorService ของเรา นั่นเป็นเพราะ Class Executors มี Predefined Factory Methods ซึ่งทำให้การสร้าง ExecutorService ง่ายขึ้น ซึ่งนี่ก็เป็นอีกตัวอย่าง:

ตอนนี้แทนที่จะทำการ ExecutorService ใน Thread เดียว แต่เรามี ExecutorService ที่มี pool ถึง 10 Threads มาดูวิธีที่เราเริ่ม Run โดยใช้ ExecutorService กัน:

โปรดสังเกตว่าเราสามารถ Submit Runnable Class ที่เราสร้างขึ้นก่อนหน้านี้ หลังจากที่ ExecutorService ถูกสร้างขึ้น นี่เป็นเพราะ ExecutorService มีคิวของตัวเองที่จะ Assign Task ให้กับ Thread ที่พร้อมใช้งาน เนื่องจาก Overhead ในการ Assign Task ให้กับ Thread ที่มีอยู่ ไม่ใช่ความรับผิดชอบของเรา เราสามารถ Submit Runnable ไปยัง ExecutorService ของเราได้อย่างต่อเนื่อง ดังที่แสดงด้านล่างนี้:

เนื่องจาก Runnable เป็น Functional Interface เราจึงสามารถใช้ Lambda Expression ของ Java 8 เพื่อสร้าง Runnable ที่ ExecutorService ของเรากำลัง Submit อยู่

จนถึงตอนนี้ เราได้ Submit Runnable ซึ่ง implements “void method run” ไปยัง ExecutorService และ Thread instances ของเราแล้ว บางครั้งเราจะต้องการ Data ที่ Return จาก Task ที่ Run ใน Thread ที่แยกต่างหาก ซึ่ง ExecutorService ช่วยให้เราสามารถทำสิ่งนี้ได้โดยใช้ Functional Interface Callable ซึ่ง Abstract Method Call ของมันมี Return Value ลองดูตัวอย่างด้านล่าง:

การเรียก Submit โดยใช้ Callable นั่นหมายความว่า เราจะได้รับผลลัพธ์เมื่อการเรียกนั้นถูก Execute ดังนั้นเพื่อให้ได้ผลลัพธ์นี้ เราจำเป็นต้องใช้ Future เนื่องจาก Submit Method ไม่ได้รอให้ Callable ทำเสร็จสมบูรณ์ก่อนที่จะย้ายไปยัง Code บรรทัดถัดไป Future ที่ถูก Return กลับมาทำให้เราสามารถเข้าถึง Methods ต่าง ๆ ที่ช่วยให้เราสามารถประมวลผล Data ที่ Return มาจาก Callable ของเรา จากตัวอย่างเราสามารถทำสิ่งนี้ได้:

ด้วยข้อมูลที่เราได้จากภาพรวมของ Runnables, Callables, Threads และ ExecutorServices เราจะสามารถเขียน Concurrent Applications และหลีกเลี่ยงการ Block บน Process ที่เลือกไว้ได้ หวังว่าบทความนี้จะมีประโยชน์ และหากคุณต้องการดู Source Code สำหรับ Tutorial นี้ สามารถดูได้ที่ GitHub นี้

ที่มา:  https://medium.com/

 

 

รับตำแหน่งงานไอทีใหม่ๆ ด้วยบริการ IT Job Alert

 

อัพเดทบทความจากคนวงในสายไอทีทาง LINE ก่อนใคร
อย่าลืมแอดไลน์ @techstarth เป็นเพื่อนนะคะ

เพิ่มเพื่อน

 

บทความล่าสุด