Java - มัลติเธรด

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

ตามความหมายแล้วการทำงานหลายอย่างพร้อมกันคือเมื่อหลายกระบวนการใช้ทรัพยากรการประมวลผลร่วมกันเช่น CPU มัลติเธรดขยายแนวคิดของการทำงานหลายอย่างพร้อมกันไปยังแอปพลิเคชันที่คุณสามารถแบ่งย่อยการดำเนินการเฉพาะภายในแอปพลิเคชันเดียวออกเป็นแต่ละเธรดได้ แต่ละเธรดสามารถทำงานแบบขนาน ระบบปฏิบัติการไม่เพียงแบ่งเวลาในการประมวลผลระหว่างแอปพลิเคชันต่างๆ

มัลติเธรดช่วยให้คุณสามารถเขียนในลักษณะที่สามารถดำเนินกิจกรรมหลายอย่างพร้อมกันในโปรแกรมเดียวกันได้

วงจรชีวิตของเธรด

เธรดผ่านขั้นตอนต่างๆในวงจรชีวิต ตัวอย่างเช่นเธรดเกิดเริ่มต้นทำงานแล้วก็ตาย แผนภาพต่อไปนี้แสดงวงจรชีวิตที่สมบูรณ์ของเธรด

ต่อไปนี้เป็นขั้นตอนของวงจรชีวิต -

  • New- เธรดใหม่เริ่มวงจรชีวิตในสถานะใหม่ มันยังคงอยู่ในสถานะนี้จนกว่าโปรแกรมจะเริ่มเธรด นอกจากนี้ยังเรียกว่าไฟล์born thread.

  • Runnable- หลังจากเธรดเกิดใหม่เริ่มต้นเธรดจะรันได้ เธรดในสถานะนี้จะถือว่ากำลังดำเนินการตามภารกิจ

  • Waiting- บางครั้งเธรดจะเปลี่ยนเป็นสถานะกำลังรอในขณะที่เธรดรอให้เธรดอื่นทำงาน เธรดจะเปลี่ยนกลับสู่สถานะรันได้ก็ต่อเมื่อเธรดอื่นส่งสัญญาณให้เธรดรอดำเนินการต่อ

  • Timed Waiting- เธรดที่รันได้สามารถเข้าสู่สถานะรอหมดเวลาสำหรับช่วงเวลาที่กำหนด เธรดในสถานะนี้จะเปลี่ยนกลับไปเป็นสถานะที่รันได้เมื่อช่วงเวลานั้นหมดอายุหรือเมื่อมีเหตุการณ์ที่รอให้เกิดขึ้น

  • Terminated (Dead) - เธรดที่รันได้เข้าสู่สถานะสิ้นสุดเมื่อทำงานเสร็จสมบูรณ์หรือถูกยกเลิก

ลำดับความสำคัญของเธรด

เธรด Java ทุกเธรดมีลำดับความสำคัญที่ช่วยให้ระบบปฏิบัติการกำหนดลำดับการจัดกำหนดการเธรด

ลำดับความสำคัญของเธรด Java อยู่ในช่วงระหว่าง MIN_PRIORITY (ค่าคงที่ 1) และ MAX_PRIORITY (ค่าคงที่ 10) ตามค่าเริ่มต้นทุกเธรดจะได้รับลำดับความสำคัญ NORM_PRIORITY (ค่าคงที่เป็น 5)

เธรดที่มีลำดับความสำคัญสูงกว่ามีความสำคัญต่อโปรแกรมมากกว่าและควรจัดสรรเวลาของตัวประมวลผลก่อนเธรดที่มีลำดับความสำคัญต่ำกว่า อย่างไรก็ตามลำดับความสำคัญของเธรดไม่สามารถรับประกันลำดับที่เธรดดำเนินการได้และขึ้นอยู่กับแพลตฟอร์มเป็นอย่างมาก

สร้างเธรดโดยใช้อินเทอร์เฟซที่รันได้

หากชั้นเรียนของคุณตั้งใจให้ดำเนินการเป็นเธรดคุณสามารถบรรลุสิ่งนี้ได้โดยใช้ไฟล์ Runnableอินเตอร์เฟซ. คุณจะต้องทำตามขั้นตอนพื้นฐานสามขั้นตอน -

ขั้นตอนที่ 1

ในขั้นตอนแรกคุณต้องใช้วิธีการ run () ที่มีให้โดยไฟล์ Runnableอินเตอร์เฟซ. วิธีนี้เป็นจุดเริ่มต้นสำหรับเธรดและคุณจะใส่ตรรกะทางธุรกิจที่สมบูรณ์ของคุณไว้ในวิธีนี้ ต่อไปนี้เป็นไวยากรณ์ง่ายๆของ run () method -

public void run( )

ขั้นตอนที่ 2

ในขั้นตอนที่สองคุณจะเริ่มต้นไฟล์ Thread วัตถุโดยใช้ตัวสร้างต่อไปนี้ -

Thread(Runnable threadObj, String threadName);

โดยที่threadObjเป็นอินสแตนซ์ของคลาสที่ใช้Runnable อินเทอร์เฟซและ threadName เป็นชื่อที่ตั้งให้กับเธรดใหม่

ขั้นตอนที่ 3

เมื่อสร้างวัตถุเธรดแล้วคุณสามารถเริ่มได้โดยการโทร start()ซึ่งเรียกใช้เมธอด call to run () ต่อไปนี้เป็นไวยากรณ์อย่างง่ายของ start () วิธีการ -

void start();

ตัวอย่าง

นี่คือตัวอย่างที่สร้างเธรดใหม่และเริ่มรัน -

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();
   }   
}

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

เอาต์พุต

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

สร้างเธรดโดยการขยายคลาสของเธรด

วิธีที่สองในการสร้างเธรดคือการสร้างคลาสใหม่ที่ขยาย Threadเรียนโดยใช้สองขั้นตอนง่ายๆต่อไปนี้ แนวทางนี้ให้ความยืดหยุ่นมากขึ้นในการจัดการเธรดหลายเธรดที่สร้างขึ้นโดยใช้วิธีการที่มีอยู่ในคลาสเธรด

ขั้นตอนที่ 1

คุณจะต้องลบล้าง run( )วิธีการที่มีอยู่ในคลาสเธรด วิธีนี้เป็นจุดเริ่มต้นสำหรับเธรดและคุณจะใส่ตรรกะทางธุรกิจที่สมบูรณ์ของคุณไว้ในวิธีนี้ ต่อไปนี้เป็นไวยากรณ์ง่ายๆของ run () method -

public void run( )

ขั้นตอนที่ 2

เมื่อสร้างวัตถุเธรดแล้วคุณสามารถเริ่มได้โดยการโทร start()ซึ่งเรียกใช้เมธอด call to run () ต่อไปนี้เป็นไวยากรณ์อย่างง่ายของ start () วิธีการ -

void start( );

ตัวอย่าง

นี่คือโปรแกรมก่อนหน้านี้ที่เขียนขึ้นใหม่เพื่อขยายเธรด -

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

เอาต์พุต

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

วิธีการด้าย

ต่อไปนี้เป็นรายการวิธีการสำคัญที่มีอยู่ในคลาสเธรด

ซีเนียร์ วิธีการและคำอธิบาย
1

public void start()

เริ่มต้นเธรดในเส้นทางที่แยกต่างหากของการดำเนินการจากนั้นเรียกใช้เมธอด run () บนอ็อบเจ็กต์เธรดนี้

2

public void run()

หากอ็อบเจ็กต์ Thread นี้ถูกสร้างอินสแตนซ์โดยใช้เป้าหมาย Runnable ที่แยกจากกันเมธอด run () จะถูกเรียกใช้บนอ็อบเจ็กต์ Runnable นั้น

3

public final void setName(String name)

เปลี่ยนชื่อของวัตถุเธรด นอกจากนี้ยังมี getName () เมธอดในการดึงชื่อ

4

public final void setPriority(int priority)

ตั้งค่าลำดับความสำคัญของวัตถุเธรดนี้ ค่าที่เป็นไปได้อยู่ระหว่าง 1 ถึง 10

5

public final void setDaemon(boolean on)

พารามิเตอร์ของ true แสดงว่าเธรดนี้เป็นเธรด daemon

6

public final void join(long millisec)

เธรดปัจจุบันเรียกใช้เมธอดนี้บนเธรดที่สองทำให้เธรดปัจจุบันถูกบล็อกจนกว่าเธรดที่สองจะสิ้นสุดหรือผ่านไปตามจำนวนมิลลิวินาทีที่ระบุ

7

public void interrupt()

ขัดจังหวะเธรดนี้ทำให้ดำเนินการต่อหากถูกบล็อกไม่ว่าด้วยเหตุผลใดก็ตาม

8

public final boolean isAlive()

ส่งคืนค่าจริงหากเธรดยังมีชีวิตอยู่ซึ่งเป็นเวลาใดก็ได้หลังจากเธรดเริ่มทำงาน แต่ก่อนที่เธรดจะทำงานจนเสร็จสิ้น

วิธีการก่อนหน้านี้ถูกเรียกใช้กับอ็อบเจ็กต์เธรดเฉพาะ วิธีการต่อไปนี้ในคลาสเธรดเป็นแบบคงที่ การเรียกใช้วิธีการแบบคงที่วิธีใดวิธีหนึ่งจะดำเนินการกับเธรดที่กำลังรันอยู่

ซีเนียร์ วิธีการและคำอธิบาย
1

public static void yield()

ทำให้เธรดที่กำลังรันอยู่เพื่อส่งผลให้กับเธรดอื่น ๆ ที่มีลำดับความสำคัญเดียวกันซึ่งกำลังรอกำหนดเวลา

2

public static void sleep(long millisec)

ทำให้เธรดที่กำลังรันอยู่ในปัจจุบันบล็อกเป็นเวลาอย่างน้อยตามจำนวนมิลลิวินาทีที่ระบุ

3

public static boolean holdsLock(Object x)

ส่งคืนจริงหากเธรดปัจจุบันมีการล็อกบนวัตถุที่กำหนด

4

public static Thread currentThread()

ส่งคืนการอ้างอิงไปยังเธรดที่กำลังรันอยู่ซึ่งเป็นเธรดที่เรียกใช้เมธอดนี้

5

public static void dumpStack()

พิมพ์การติดตามสแต็กสำหรับเธรดที่กำลังรันอยู่ซึ่งมีประโยชน์เมื่อทำการดีบักแอ็พพลิเคชันแบบมัลติเธรด

ตัวอย่าง

โปรแกรม ThreadClassDemo ต่อไปนี้สาธิตวิธีการเหล่านี้บางส่วนของคลาส Thread พิจารณาชั้นเรียนDisplayMessage ซึ่งดำเนินการ Runnable -

// File Name : DisplayMessage.java
// Create a thread to implement Runnable

public class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
      this.message = message;
   }
   
   public void run() {
      while(true) {
         System.out.println(message);
      }
   }
}

ต่อไปนี้เป็นคลาสอื่นที่ขยายคลาสเธรด -

// File Name : GuessANumber.java
// Create a thread to extentd Thread

public class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
      this.number = number;
   }
   
   public void run() {
      int counter = 0;
      int guess = 0;
      do {
         guess = (int) (Math.random() * 100 + 1);
         System.out.println(this.getName() + " guesses " + guess);
         counter++;
      } while(guess != number);
      System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}

ต่อไปนี้เป็นโปรแกรมหลักซึ่งใช้ประโยชน์จากคลาสที่กำหนดไว้ข้างต้น -

// File Name : ThreadClassDemo.java
public class ThreadClassDemo {

   public static void main(String [] args) {
      Runnable hello = new DisplayMessage("Hello");
      Thread thread1 = new Thread(hello);
      thread1.setDaemon(true);
      thread1.setName("hello");
      System.out.println("Starting hello thread...");
      thread1.start();
      
      Runnable bye = new DisplayMessage("Goodbye");
      Thread thread2 = new Thread(bye);
      thread2.setPriority(Thread.MIN_PRIORITY);
      thread2.setDaemon(true);
      System.out.println("Starting goodbye thread...");
      thread2.start();

      System.out.println("Starting thread3...");
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try {
         thread3.join();
      } catch (InterruptedException e) {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread4...");
      Thread thread4 = new GuessANumber(75);
      
      thread4.start();
      System.out.println("main() is ending...");
   }
}

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ คุณสามารถลองตัวอย่างนี้ซ้ำแล้วซ้ำอีกและคุณจะได้ผลลัพธ์ที่แตกต่างกันทุกครั้ง

เอาต์พุต

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

แนวคิดเกี่ยวกับ Java Multithreading ที่สำคัญ

ในขณะที่ทำการเขียนโปรแกรม Multithreading ใน Java คุณจะต้องมีแนวคิดต่อไปนี้ที่มีประโยชน์มาก -

  • การซิงโครไนซ์เธรดคืออะไร?

  • การจัดการการสื่อสารระหว่างเธรด

  • การจัดการการหยุดชะงักของเธรด

  • การทำงานของเธรดหลัก