- ทำความเข้าใจเธรด (Threads)
- มัลติเธรด (Multithreading) ใน Java
- การซิงโครไนซ์เธรด (Thread Synchronization)
ในโลกของการทำแอพ การดำเนินการงานพร้อมกัน (Concurrency) เป็นข้อกำหนดที่แพร่หลาย แอปพลิเคชันสมัยใหม่ส่วนใหญ่มักต้องทำหลายอย่างพร้อมกัน ไม่ว่าจะเป็นการจัดการผู้ใช้หลายคน เรียกใช้งานพื้นหลัง หรือดำเนินการคำนวณที่ซับซ้อน
ใน Java วิธีหนึ่งในการบรรลุการทำงานพร้อมกันคือการใช้เธรด (Threads) และมัลติเธรด (Multithreading) การทำความเข้าใจวิธีใช้คุณสมบัติเหล่านี้สามารถช่วยปรับปรุงประสิทธิภาพและการตอบสนองของการทำแอพ Java ของคุณได้
1. ทำความเข้าใจเธรด (Threads)
เธรดในบริบทของการทำแอพ Java คือหน่วยการดำเนินการที่เล็กที่สุด มันเหมือนกับการดำเนินการที่แยกจากกัน แอปพลิเคชันสามารถมีเธรดหลายเธรดที่ทำงานพร้อมกันได้ ซึ่งมีประโยชน์อย่างเหลือเชื่อสำหรับการทำงานที่สามารถรันพร้อมกันได้
Java ให้การสนับสนุนในตัวสำหรับการเขียนโปรแกรมแบบมัลติเธรด ซึ่งเป็นคุณสมบัติที่จำเป็นในการทำแอพแบบโต้ตอบและเครือข่าย
เธรด Java เป็นวัตถุ (Object) ของคลาส java.lang.Thread คลาสนี้มีคอนสตรัคเตอร์และเมธอดในการสร้างและดำเนินการกับเธรด แต่ละเธรดเริ่มต้นด้วย call stack เดียวและดำเนินการตามคำสั่งตามกำหนดการของเธรด
มาทำแอพ Java อย่างง่ายโดยใช้เธรด:
class SimpleThread extends Thread {
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getId() + " Value " + i);
}
}
}
public class MainApp {
public static void main(String args[]) {
SimpleThread thread1 = new SimpleThread();
thread1.start();
SimpleThread thread2 = new SimpleThread();
thread2.start();
}
}
ในตัวอย่างนี้ เรากำลังสร้างและเริ่มต้นสองเธรด แต่ละเธรดจะพิมพ์ ID ของเธรดปัจจุบันพร้อมกับค่าของตัวแปรตัวนับ การเรียกใช้แอปพลิเคชันนี้ควรแสดงเอาต์พุตแบบแทรกระหว่างเธรดทั้งสอง
2. มัลติเธรด (Multithreading) ใน Java
มัลติเธรดเป็นรูปแบบพิเศษของการทำงานหลายอย่างพร้อมกันโดยที่หลายเธรดจากกระบวนการเดียวกันใช้แกน CPU เดียวกันร่วมกัน มัลติเธรดเป็นแกนหลักของ Java และไลบรารีและเฟรมเวิร์กจำนวนมาก อันที่จริง แม้แต่โปรแกรม Java ทั่วไปก็มีเธรดหลายเธรดทำงานในพื้นหลังที่จัดการงานต่างๆ เช่น การรวบรวมขยะ (garbage collection)
การทำงานแบบมัลติเธรดช่วยให้สามารถทำงานแบบขนานได้ ทำให้ใช้วงจร CPU ได้อย่างมีประสิทธิภาพ ข้อได้เปรียบหลักของมัลติเธรด ได้แก่ การตอบสนองเชิงโต้ตอบที่ดีขึ้น ปริมาณงานที่สูงขึ้น การสร้างแบบจำลองที่ง่ายขึ้น และการใช้ประโยชน์จากสถาปัตยกรรมมัลติโปรเซสเซอร์ที่ดีขึ้น
มัลติเธรดสามารถทำได้ใน Java สองวิธี:
- โดยการขยายคลาส
Thread
- โดยใช้อินเตอร์เฟส
Runnable
แม้ว่าทั้งสองวิธีจะบรรลุวัตถุประสงค์เดียวกัน แต่การใช้ Runnable
อินเทอร์เฟซเป็นเรื่องปกติมากขึ้นเนื่องจาก Java ไม่รองรับการสืบทอดหลายรายการ หมายความว่าคลาสหนึ่งไม่สามารถขยายได้มากกว่าหนึ่งคลาส
นี่คือตัวอย่างของการทำงานแบบมัลติเธรดโดยใช้ Runnable
:
class SimpleRunnable implements Runnable {
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getId() + " Value " + i);
}
}
}
public class MainApp {
public static void main(String args[]) {
Thread thread1 = new Thread(new SimpleRunnable());
thread1.start();
Thread thread2 = new Thread(new SimpleRunnable());
thread2.start();
}
}
ในตัวอย่างนี้ SimpleRunnable
คลาสใช้ Runnable
อินเทอร์เฟซและแทนที่ run
เมธอด เมธอด run
ประกอบด้วยโค้ดที่จะดำเนินการเมื่อเธรดเริ่มทำงาน
3. การซิงโครไนซ์เธรด (Thread Synchronization)
ในแอปพลิเคชันแบบมัลติเธรด เธรดจำนวนมากมักจำเป็นต้องเข้าถึงทรัพยากรหรือข้อมูลที่ใช้ร่วมกัน หากไม่มีการจัดการที่เหมาะสม การเข้าถึงพร้อมกันอาจนำไปสู่ความไม่สอดคล้องกันหรือจุดบกพร่อง เช่น สภาวะการแข่งขัน (race conditions)
การซิงโครไนซ์เธรดช่วยให้มั่นใจได้ว่าเธรดเดียวเท่านั้นที่สามารถเข้าถึงทรัพยากรที่ใช้ร่วมกันในแต่ละครั้ง โดยรักษาความสอดคล้องของข้อมูล ใน Java การซิงโครไนซ์เธรดทำได้โดยใช้ synchronized
บล็อกหรือเมธอด
คำหลัก synchronized
ใน Java สร้างบล็อกของโค้ดที่อ้างถึงเป็นส่วนที่สำคัญ มีเพียงหนึ่งเธรดเท่านั้นที่สามารถดำเนินการส่วนที่สำคัญนี้ได้ในแต่ละครั้ง เพื่อให้มั่นใจถึงความสมบูรณ์ของข้อมูล
นี่คือตัวอย่างของวิธีการซิงโครไนซ์ใน Java:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class MainApp {
public static void main(String args[]) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> {
for(int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for(int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
// Wait for both threads to finish
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count is: " + counter.getCount());
}
}
ในตัวอย่างนี้ increment
เมธอด ใน Counter
คลาสจะถูกซิงโครไนซ์ ซึ่งหมายความว่าหากเธรดหนึ่งกำลังดำเนินการ increment
เมธอด เธรดอื่นๆ ทั้งหมดที่ต้องการดำเนินการเมธอดนี้จะถูกบล็อกจนกว่าเธรดแรกจะออกจากเมธอด ด้วยวิธีนี้ เราป้องกันสภาวะการแข่งขันและทำให้แน่ใจว่าค่านับสุดท้ายของเรานั้นถูกต้อง
มัลติเธรดใน Java เป็นคุณลักษณะอันทรงพลังที่สามารถปรับปรุงประสิทธิภาพและการตอบสนองของแอปพลิเคชันของคุณได้อย่างมาก เมื่อเข้าใจวิธีการทำงานของเธรดและวิธีใช้งานอย่างมีประสิทธิภาพ คุณจะสามารถทำแอพ Java ที่มีประสิทธิภาพและแข็งแกร่งได้
โปรดจำไว้ว่าการทำงานแบบมัลติเธรดมาพร้อมกับชุดของความท้าทาย เช่น ความไม่สอดคล้องกันของข้อมูลและการรบกวนของเธรด แต่ด้วยการใช้กลไกการซิงโครไนซ์ที่มีให้โดย Java คุณสามารถควบคุมการเข้าถึงทรัพยากรที่ใช้ร่วมกันและรักษาความสมบูรณ์ของข้อมูลของคุณได้
เมื่อคุณเจาะลึกลงไปใน Java คุณจะพบกับสถานการณ์ที่ซับซ้อนมากขึ้นซึ่งเกี่ยวข้องกับเธรดและมัลติเธรด อย่างไรก็ตาม ความเข้าใจพื้นฐานนี้จะเป็นรากฐานที่มั่นคงสำหรับคุณในการทำแอพ