Java คือ ตอนที่ 11 : การทำงานพร้อมกัน (Concurrency)

  1. ทำความเข้าใจเธรด (Threads)
  2. มัลติเธรด (Multithreading) ใน Java
  3. การซิงโครไนซ์เธรด (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 สองวิธี:

  1. โดยการขยายคลาส Thread
  2. โดยใช้อินเตอร์เฟส 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 คุณจะพบกับสถานการณ์ที่ซับซ้อนมากขึ้นซึ่งเกี่ยวข้องกับเธรดและมัลติเธรด อย่างไรก็ตาม ความเข้าใจพื้นฐานนี้จะเป็นรากฐานที่มั่นคงสำหรับคุณในการทำแอพ


Java คืออะไร

Java คือ ตอนที่ 10 : ไลบรารี (Libraries) และเฟรมเวิร์ก (Frameworks)
Java คือ ตอนที่ 12 : ระบบเครือข่าย (Networking)