Java Concurrency - คู่มือฉบับย่อ

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

ตามความหมายแล้วการทำงานหลายอย่างพร้อมกันคือเมื่อหลายกระบวนการใช้ทรัพยากรการประมวลผลร่วมกันเช่น 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

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

void start();

Example

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

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();
   }   
}

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

Output

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

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

void start( );

Example

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

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();
   }   
}

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

Output

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.

ในบทนี้เราจะพูดถึงแง่มุมต่างๆของการตั้งค่าสภาพแวดล้อมที่เหมาะสำหรับ Java

การตั้งค่าสภาพแวดล้อมท้องถิ่น

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

Java SE เป็นอิสระที่มีอยู่จากการเชื่อมโยงดาวน์โหลด Java คุณสามารถดาวน์โหลดเวอร์ชันตามระบบปฏิบัติการของคุณ

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

การตั้งค่าเส้นทางสำหรับ Windows

สมมติว่าคุณติดตั้ง Java ในไดเร็กทอรีc: \ Program Files \ java \ jdk -

  • คลิกขวาที่ 'My Computer' และเลือก 'Properties'

  • คลิกปุ่ม "ตัวแปรสภาพแวดล้อม" ใต้แท็บ "ขั้นสูง"

  • ตอนนี้เปลี่ยนตัวแปร 'Path' เพื่อให้มีพา ธ ไปยังไฟล์ปฏิบัติการ Java ตัวอย่างหากเส้นทางถูกตั้งค่าเป็น 'C: \ WINDOWS \ SYSTEM32' ให้เปลี่ยนเส้นทางของคุณเป็นอ่าน 'C: \ WINDOWS \ SYSTEM32; c: \ Program Files \ java \ jdk \ bin'

การตั้งค่าเส้นทางสำหรับ Linux, UNIX, Solaris, FreeBSD

ควรตั้งค่า PATH ตัวแปรสภาพแวดล้อมให้ชี้ไปที่ตำแหน่งที่ติดตั้งไบนารี Java โปรดดูเอกสารประกอบเชลล์ของคุณหากคุณมีปัญหาในการดำเนินการนี้

ตัวอย่างเช่นหากคุณใช้bashเป็นเชลล์ของคุณคุณจะต้องเพิ่มบรรทัดต่อไปนี้ต่อท้าย '.bashrc: export PATH = / path / to / java: $ PATH'

บรรณาธิการ Java ยอดนิยม

ในการเขียนโปรแกรม Java ของคุณคุณจะต้องมีโปรแกรมแก้ไขข้อความ มี IDE ที่ซับซ้อนมากขึ้นในตลาด แต่ในตอนนี้คุณสามารถพิจารณาข้อใดข้อหนึ่งต่อไปนี้ -

  • Notepad - บนเครื่อง Windows คุณสามารถใช้โปรแกรมแก้ไขข้อความง่ายๆเช่น Notepad (แนะนำสำหรับบทช่วยสอนนี้), TextPad

  • Netbeans - Java IDE ที่เป็นโอเพ่นซอร์สและฟรีซึ่งสามารถดาวน์โหลดได้จาก https://netbeans.org/index.html.

  • Eclipse - Java IDE ที่พัฒนาโดยชุมชนโอเพนซอร์ส eclipse และสามารถดาวน์โหลดได้จาก https://www.eclipse.org/.

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

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

public void suspend()

วิธีนี้ทำให้เธรดอยู่ในสถานะที่ถูกระงับและสามารถกลับมาทำงานต่อได้โดยใช้เมธอด resume ()

2

public void stop()

วิธีนี้จะหยุดเธรดโดยสิ้นเชิง

3

public void resume()

วิธีนี้จะดำเนินการต่อเธรดซึ่งถูกระงับโดยใช้วิธีการ Suspend ()

4

public void wait()

ทำให้เธรดปัจจุบันรอจนกว่าเธรดอื่นจะเรียกใช้การแจ้งเตือน ()

5

public void notify()

ปลุกเธรดเดียวที่รออยู่บนจอภาพของวัตถุนี้

โปรดทราบว่า Java เวอร์ชันล่าสุดได้เลิกใช้งานวิธีการ Suspend (), resume () และ stop () แล้วดังนั้นคุณต้องใช้ทางเลือกอื่นที่มีอยู่

ตัวอย่าง

class RunnableDemo implements Runnable {
   public Thread t;
   private String threadName;
   boolean suspended = false;

   RunnableDemo(String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );

      try {
         
         for(int i = 10; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);

            // Let the thread sleep for a while.
            Thread.sleep(300);

            synchronized(this) {
               
               while(suspended) {
                  wait();
               }
            }
         }
      } 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 ();
      }
   }
   
   void suspend() {
      suspended = true;
   }
   
   synchronized void resume() {
      suspended = false;
      notify();
   }
}

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();

      try {
         Thread.sleep(1000);
         R1.suspend();
         System.out.println("Suspending First Thread");
         Thread.sleep(1000);
         R1.resume();
         System.out.println("Resuming First Thread");
         
         R2.suspend();
         System.out.println("Suspending thread Two");
         Thread.sleep(1000);
         R2.resume();
         System.out.println("Resuming thread Two");
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      } try {
         System.out.println("Waiting for threads to finish.");
         R1.t.join();
         R2.t.join();
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }
      System.out.println("Main thread exiting.");
   }
}

โปรแกรมข้างต้นสร้างผลลัพธ์ต่อไปนี้ -

เอาต์พุต

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.

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

มีสามวิธีง่ายๆและเคล็ดลับเล็ก ๆ น้อย ๆ ที่ทำให้การสื่อสารเธรดเป็นไปได้ ทั้งสามวิธีดังต่อไปนี้ -

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

public void wait()

ทำให้เธรดปัจจุบันรอจนกว่าเธรดอื่นจะเรียกใช้การแจ้งเตือน ()

2

public void notify()

ปลุกเธรดเดียวที่รออยู่บนจอภาพของวัตถุนี้

3

public void notifyAll()

ปลุกเธรดทั้งหมดที่เรียกว่า wait () บนอ็อบเจ็กต์เดียวกัน

วิธีการเหล่านี้ได้รับการดำเนินการเป็น finalวิธีการใน Object ดังนั้นจึงมีอยู่ในทุกคลาส ทั้งสามวิธีสามารถเรียกใช้ได้จากภายในไฟล์synchronized บริบท.

ตัวอย่าง

ตัวอย่างนี้แสดงวิธีที่สองเธรดสามารถสื่อสารโดยใช้ wait() และ notify()วิธี. คุณสามารถสร้างระบบที่ซับซ้อนโดยใช้แนวคิดเดียวกัน

class Chat {
   boolean flag = false;

   public synchronized void Question(String msg) {

      if (flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = true;
      notify();
   }

   public synchronized void Answer(String msg) {

      if (!flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = false;
      notify();
   }
}

class T1 implements Runnable {
   Chat m;
   String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };

   public T1(Chat m1) {
      this.m = m1;
      new Thread(this, "Question").start();
   }

   public void run() {
   
      for (int i = 0; i < s1.length; i++) {
         m.Question(s1[i]);
      }
   }
}

class T2 implements Runnable {
   Chat m;
   String[] s2 = { "Hi", "I am good, what about you?", "Great!" };

   public T2(Chat m2) {
      this.m = m2;
      new Thread(this, "Answer").start();
   }

   public void run() {

      for (int i = 0; i < s2.length; i++) {
         m.Answer(s2[i]);
      }
   }
}

public class TestThread {

   public static void main(String[] args) {
      Chat m = new Chat();
      new T1(m);
      new T2(m);
   }
}

เมื่อโปรแกรมข้างต้นได้รับการปฏิบัติตามและดำเนินการโปรแกรมจะให้ผลลัพธ์ดังต่อไปนี้ -

เอาต์พุต

Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!

ตัวอย่างด้านบนได้ถูกนำมาใช้และแก้ไขแล้วจาก [https://stackoverflow.com/questions/2170520/inter-thread-communication-in-java]

ตัวอย่างมัลติเธรดที่มีการซิงโครไนซ์

นี่คือตัวอย่างเดียวกับที่พิมพ์ค่าตัวนับตามลำดับและทุกครั้งที่เราเรียกใช้มันจะให้ผลลัพธ์เดียวกัน

ตัวอย่าง

class PrintDemo {
   
   public void printCount() {
      
      try {
         
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo(String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      
      synchronized(PD) {
         PD.printCount();
      }
      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[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo T2 = new ThreadDemo("Thread - 2 ", PD);

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
   }
}

สิ่งนี้ให้ผลลัพธ์เดียวกันทุกครั้งที่คุณเรียกใช้โปรแกรมนี้ -

เอาต์พุต

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

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

ตัวอย่าง

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
   
      public void run() {
      
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }

   private static class ThreadDemo2 extends Thread {
   
      public void run() {
      
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

เมื่อคุณคอมไพล์และรันโปรแกรมข้างต้นคุณจะพบสถานการณ์ชะงักงันและต่อไปนี้คือผลลัพธ์ที่สร้างโดยโปรแกรม -

เอาต์พุต

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

โปรแกรมด้านบนจะหยุดทำงานตลอดไปเนื่องจากเธรดทั้งสองไม่อยู่ในตำแหน่งที่จะดำเนินการต่อและรอให้กันและกันคลายล็อกคุณจึงสามารถออกจากโปรแกรมได้โดยกด CTRL + C

ตัวอย่างโซลูชัน Deadlock

มาเปลี่ยนลำดับของการล็อกและรันโปรแกรมเดียวกันเพื่อดูว่าเธรดทั้งสองยังคงรอกันอยู่หรือไม่ -

ตัวอย่าง

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
   
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }

   private static class ThreadDemo2 extends Thread {
      
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");
           
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

ดังนั้นการเปลี่ยนลำดับของการล็อกจะป้องกันไม่ให้โปรแกรมเข้าสู่สถานการณ์ชะงักงันและดำเนินการตามผลลัพธ์ต่อไปนี้ -

เอาต์พุต

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

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

คลาส ThreadLocal ใช้เพื่อสร้างตัวแปรโลคัลเธรดซึ่งสามารถอ่านและเขียนโดยเธรดเดียวกันเท่านั้น ตัวอย่างเช่นหากสองเธรดกำลังเข้าถึงโค้ดที่มีการอ้างอิงถึงตัวแปร threadLocal เดียวกันแต่ละเธรดจะไม่เห็นการแก้ไขใด ๆ กับตัวแปร threadLocal ที่ทำโดยเธรดอื่น

วิธีการ ThreadLocal

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

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

public T get()

ส่งคืนค่าในสำเนาของเธรดปัจจุบันของตัวแปรเธรดโลคัลนี้

2

protected T initialValue()

ส่งคืน "ค่าเริ่มต้น" ของเธรดปัจจุบันสำหรับตัวแปรเธรดโลคัลนี้

3

public void remove()

ลบค่าของเธรดปัจจุบันสำหรับตัวแปรเธรดโลคัลนี้

4

public void set(T value)

ตั้งค่าสำเนาของเธรดปัจจุบันของตัวแปรเธรดโลคัลนี้เป็นค่าที่ระบุ

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงให้เห็นถึงวิธีการเหล่านี้บางส่วนของคลาส ThreadLocal ที่นี่เราใช้ตัวแปรตัวนับสองตัวตัวแปรหนึ่งเป็นตัวแปรปกติและอีกตัวแปรหนึ่งคือ ThreadLocal

class RunnableDemo implements Runnable {
   int counter;
   ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>();

   public void run() {     
      counter++;

      if(threadLocalCounter.get() != null) {
         threadLocalCounter.set(threadLocalCounter.get().intValue() + 1);
      } else {
         threadLocalCounter.set(0);
      }
      System.out.println("Counter: " + counter);
      System.out.println("threadLocalCounter: " + threadLocalCounter.get());
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo commonInstance = new RunnableDemo();

      Thread t1 = new Thread(commonInstance);
      Thread t2 = new Thread(commonInstance);
      Thread t3 = new Thread(commonInstance);
      Thread t4 = new Thread(commonInstance);

      t1.start();
      t2.start();
      t3.start();
      t4.start();

      // wait for threads to end
      try {
         t1.join();
         t2.join();
         t3.join();
         t4.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
   }
}

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

เอาต์พุต

Counter: 1
threadLocalCounter: 0
Counter: 2
threadLocalCounter: 0
Counter: 3
threadLocalCounter: 0
Counter: 4
threadLocalCounter: 0

คุณสามารถเห็นค่าของตัวนับเพิ่มขึ้นตามแต่ละเธรด แต่ threadLocalCounter ยังคงเป็น 0 สำหรับแต่ละเธรด

java.util.concurrent.ThreadLocalRandom เป็นคลาสยูทิลิตี้ที่นำมาใช้ตั้งแต่ jdk 1.7 เป็นต้นไปและมีประโยชน์เมื่อต้องใช้หลายเธรดหรือ ForkJoinTasks เพื่อสร้างตัวเลขแบบสุ่ม ปรับปรุงประสิทธิภาพและมีความขัดแย้งน้อยกว่าวิธี Math.random ()

วิธี ThreadLocalRandom

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

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

public static ThreadLocalRandom current()

ส่งคืน ThreadLocalRandom ของเธรดปัจจุบัน

2

protected int next(int bits)

สร้างหมายเลขเทียมถัดไป

3

public double nextDouble(double n)

ส่งคืน pseudorandom ค่าคู่ที่กระจายสม่ำเสมอระหว่าง 0 (รวม) และค่าที่ระบุ (ไม่รวม)

4

public double nextDouble(double least, double bound)

ส่งคืนค่าเทียมที่มีการกระจายอย่างสม่ำเสมอระหว่างค่าน้อยที่สุดที่กำหนด (รวม) และขอบเขต (ไม่รวม)

5

public int nextInt(int least, int bound)

ส่งคืนค่าเทียมที่มีการกระจายอย่างสม่ำเสมอระหว่างค่าน้อยที่สุดที่กำหนด (รวม) และขอบเขต (ไม่รวม)

6

public long nextLong(long n)

ส่งคืน pseudorandom ค่าที่กระจายอย่างสม่ำเสมอระหว่าง 0 (รวม) และค่าที่ระบุ (ไม่รวม)

7

public long nextLong(long least, long bound)

ส่งคืนค่าเทียมที่มีการกระจายอย่างสม่ำเสมอระหว่างค่าน้อยที่สุดที่กำหนด (รวม) และขอบเขต (ไม่รวม)

8

public void setSeed(long seed)

พ่น UnsupportedOperationException

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้สาธิตวิธีการเหล่านี้บางส่วนของอินเทอร์เฟซ Lock ที่นี่เราใช้ lock () เพื่อรับล็อคและปลดล็อค () เพื่อคลายล็อก

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ThreadLocalRandom;

public class TestThread {
  
   public static void main(final String[] arguments) {
      System.out.println("Random Integer: " + new Random().nextInt());  
      System.out.println("Seeded Random Integer: " + new Random(15).nextInt());  
      System.out.println(
         "Thread Local Random Integer: " + ThreadLocalRandom.current().nextInt());
      
      final ThreadLocalRandom random = ThreadLocalRandom.current();  
      random.setSeed(15); //exception will come as seeding is not allowed in ThreadLocalRandom.
      System.out.println("Seeded Thread Local Random Integer: " + random.nextInt());  
   }
}

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

เอาต์พุต

Random Integer: 1566889198
Seeded Random Integer: -1159716814
Thread Local Random Integer: 358693993
Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.concurrent.ThreadLocalRandom.setSeed(Unknown Source)
        at TestThread.main(TestThread.java:21)

ที่นี่เราใช้คลาส ThreadLocalRandom และ Random เพื่อรับตัวเลขสุ่ม

อินเตอร์เฟส java.util.concurrent.locks.Lock ถูกใช้เพื่อเป็นกลไกการซิงโครไนซ์เธรดที่คล้ายกับบล็อกที่ซิงโครไนซ์ กลไกการล็อคแบบใหม่มีความยืดหยุ่นมากกว่าและมีตัวเลือกมากกว่าบล็อกที่ซิงโครไนซ์ ความแตกต่างหลักระหว่างล็อคและบล็อกซิงโครไนซ์มีดังต่อไปนี้ -

  • Guarantee of sequence- บล็อกที่ซิงโครไนซ์ไม่รับประกันลำดับใด ๆ ที่เธรดรอจะได้รับการเข้าถึง ล็อคอินเทอร์เฟซจัดการมัน

  • No timeout- บล็อกที่ซิงโครไนซ์ไม่มีตัวเลือกของการหมดเวลาหากไม่ได้รับการล็อค ล็อคอินเทอร์เฟซมีตัวเลือกดังกล่าว

  • Single method - บล็อกที่ซิงโครไนซ์จะต้องมีอยู่อย่างสมบูรณ์ภายในวิธีการเดียวในขณะที่สามารถเรียกใช้วิธีการล็อก () และปลดล็อก () ของอินเทอร์เฟซล็อคด้วยวิธีการต่างๆ

วิธีการล็อค

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

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

public void lock()

ได้รับการล็อค

2

public void lockInterruptibly()

ได้รับการล็อกเว้นแต่เธรดปัจจุบันจะถูกขัดจังหวะ

3

public Condition newCondition()

ส่งคืนอินสแตนซ์ Condition ใหม่ที่เชื่อมโยงกับอินสแตนซ์ Lock นี้

4

public boolean tryLock()

จะได้รับการล็อคก็ต่อเมื่อมันว่างในเวลาที่เรียกใช้

5

public boolean tryLock()

จะได้รับการล็อคก็ต่อเมื่อมันว่างในเวลาที่เรียกใช้

6

public boolean tryLock(long time, TimeUnit unit)

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

7

public void unlock()

ปลดล็อค

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้สาธิตวิธีการเหล่านี้บางส่วนของอินเทอร์เฟซ Lock ที่นี่เราใช้ lock () เพื่อรับล็อคและปลดล็อค () เพื่อคลายล็อก

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class PrintDemo {
   private final Lock queueLock = new ReentrantLock();

   public void print() {
      queueLock.lock();

      try {
         Long duration = (long) (Math.random() * 10000);
         System.out.println(Thread.currentThread().getName() 
            + "  Time Taken " + (duration / 1000) + " seconds.");
         Thread.sleep(duration);
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         System.out.printf(
            "%s printed the document successfully.\n", Thread.currentThread().getName());
         queueLock.unlock();
      }
   }
}

class ThreadDemo extends Thread {
   PrintDemo  printDemo;

   ThreadDemo(String name,  PrintDemo printDemo) {
      super(name);
      this.printDemo = printDemo;
   }   

   @Override
   public void run() {
      System.out.printf(
         "%s starts printing a document\n", Thread.currentThread().getName());
      printDemo.print();
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo t1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo t2 = new ThreadDemo("Thread - 2 ", PD);
      ThreadDemo t3 = new ThreadDemo("Thread - 3 ", PD);
      ThreadDemo t4 = new ThreadDemo("Thread - 4 ", PD);

      t1.start();
      t2.start();
      t3.start();
      t4.start();
   }
}

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

เอาต์พุต

Thread - 1  starts printing a document
Thread - 4  starts printing a document
Thread - 3  starts printing a document
Thread - 2  starts printing a document
Thread - 1   Time Taken 4 seconds.
Thread - 1  printed the document successfully.
Thread - 4   Time Taken 3 seconds.
Thread - 4  printed the document successfully.
Thread - 3   Time Taken 5 seconds.
Thread - 3  printed the document successfully.
Thread - 2   Time Taken 4 seconds.
Thread - 2  printed the document successfully.

เราใช้คลาส ReentrantLock เป็นการใช้งานอินเทอร์เฟซ Lock ที่นี่ คลาส ReentrantLock อนุญาตให้เธรดล็อกเมธอดแม้ว่าจะมีการล็อกเมธอดอื่นอยู่แล้วก็ตาม

อินเทอร์เฟซ java.util.concurrent.locks.ReadWriteLock อนุญาตให้อ่านหลายเธรดพร้อมกัน แต่สามารถเขียนเธรดได้ครั้งละหนึ่งเธรดเท่านั้น

  • Read Lock - หากไม่มีเธรดล็อก ReadWriteLock สำหรับการเขียนเธรดหลายเธรดจะสามารถเข้าถึงล็อกการอ่านได้

  • Write Lock - หากไม่มีเธรดกำลังอ่านหรือเขียนเธรดหนึ่งเธรดสามารถเข้าถึงล็อกการเขียนได้

วิธีการล็อค

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

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

public Lock readLock()

ส่งคืนล็อคที่ใช้สำหรับการอ่าน

2

public Lock writeLock()

ส่งคืนล็อกที่ใช้สำหรับการเขียน

ตัวอย่าง

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestThread {
   private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
   private static String message = "a";

   public static void main(String[] args) throws InterruptedException {
      Thread t1 = new Thread(new WriterA());
      t1.setName("Writer A");
      
      Thread t2 = new Thread(new WriterB());
      t2.setName("Writer B");
      
      Thread t3 = new Thread(new Reader());
      t3.setName("Reader");
      t1.start();
      t2.start();
      t3.start();
      t1.join();
      t2.join();
      t3.join();
   }

   static class Reader implements Runnable {

      public void run() {
         
         if(lock.isWriteLocked()) {
            System.out.println("Write Lock Present.");
         }
         lock.readLock().lock();

         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            System.out.println(Thread.currentThread().getName() +": "+ message );
            lock.readLock().unlock();
         }
      }
   }

   static class WriterA implements Runnable {

      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("a");
            lock.writeLock().unlock();
         }
      }
   }

   static class WriterB implements Runnable {

      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("b");
            lock.writeLock().unlock();
         }
      }
   }
}

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

เอาต์พุต

Writer A  Time Taken 6 seconds.
Write Lock Present.
Writer B  Time Taken 2 seconds.
Reader  Time Taken 0 seconds.
Reader: aab

อินเทอร์เฟซ java.util.concurrent.locks.Condition จัดเตรียมความสามารถของเธรดเพื่อระงับการดำเนินการจนกว่าเงื่อนไขที่กำหนดจะเป็นจริง วัตถุเงื่อนไขจำเป็นต้องถูกผูกไว้กับ Lock และจะได้รับโดยใช้เมธอด newCondition ()

เงื่อนไขวิธีการ

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

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

public void await()

ทำให้เธรดปัจจุบันรอจนกว่าจะมีการส่งสัญญาณหรือถูกขัดจังหวะ

2

public boolean await(long time, TimeUnit unit)

ทำให้เธรดปัจจุบันรอจนกว่าจะมีการส่งสัญญาณหรือถูกขัดจังหวะหรือเวลาในการรอที่ระบุผ่านไป

3

public long awaitNanos(long nanosTimeout)

ทำให้เธรดปัจจุบันรอจนกว่าจะมีการส่งสัญญาณหรือถูกขัดจังหวะหรือเวลาในการรอที่ระบุผ่านไป

4

public long awaitUninterruptibly()

ทำให้เธรดปัจจุบันรอจนกว่าจะมีการส่งสัญญาณ

5

public long awaitUntil()

ทำให้เธรดปัจจุบันรอจนกว่าจะมีการส่งสัญญาณหรือถูกขัดจังหวะหรือพ้นกำหนดเวลาที่ระบุ

6

public void signal()

ตื่นขึ้นมาหนึ่งกระทู้รอ

7

public void signalAll()

ตื่นขึ้นมาทุกหัวข้อที่รอคอย

ตัวอย่าง

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestThread {

   public static void main(String[] args) throws InterruptedException {
      ItemQueue itemQueue = new ItemQueue(10);

      //Create a producer and a consumer.
      Thread producer = new Producer(itemQueue);
      Thread consumer = new Consumer(itemQueue);

      //Start both threads.
      producer.start();
      consumer.start();

      //Wait for both threads to terminate.
      producer.join();
      consumer.join();
   }

   static class ItemQueue {
      private Object[] items = null;
      private int current = 0;
      private int placeIndex = 0;
      private int removeIndex = 0;

      private final Lock lock;
      private final Condition isEmpty;
      private final Condition isFull;

      public ItemQueue(int capacity) {
         this.items = new Object[capacity];
         lock = new ReentrantLock();
         isEmpty = lock.newCondition();
         isFull = lock.newCondition();
      }

      public void add(Object item) throws InterruptedException {
         lock.lock();

         while(current >= items.length)
            isFull.await();

         items[placeIndex] = item;
         placeIndex = (placeIndex + 1) % items.length;
         ++current;

         //Notify the consumer that there is data available.
         isEmpty.signal();
         lock.unlock();
      }

      public Object remove() throws InterruptedException {
         Object item = null;

         lock.lock();

         while(current <= 0) {
            isEmpty.await();
         }
         item = items[removeIndex];
         removeIndex = (removeIndex + 1) % items.length;
         --current;

         //Notify the producer that there is space available.
         isFull.signal();
         lock.unlock();

         return item;
      }

      public boolean isEmpty() {
         return (items.length == 0);
      }
   }

   static class Producer extends Thread {
      private final ItemQueue queue;
      
      public Producer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         String[] numbers =
            {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};

         try {
            
            for(String number: numbers) {
               System.out.println("[Producer]: " + number);
            }
            queue.add(null);
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         } 
      }
   }

   static class Consumer extends Thread {
      private final ItemQueue queue;
      
      public Consumer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         
         try {
            
            do {
               Object number = queue.remove();
               System.out.println("[Consumer]: " + number);

               if(number == null) {
                  return;
               }
            } while(!queue.isEmpty());
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         }
      }
   }
}

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

เอาต์พุต

[Producer]: 1
[Producer]: 2
[Producer]: 3
[Producer]: 4
[Producer]: 5
[Producer]: 6
[Producer]: 7
[Producer]: 8
[Producer]: 9
[Producer]: 10
[Producer]: 11
[Producer]: 12
[Consumer]: null

คลาส java.util.concurrent.atomic AtomicInteger จัดเตรียมการดำเนินการกับค่า int พื้นฐานที่สามารถอ่านและเขียนแบบอะตอมได้และยังมีการดำเนินการของอะตอมขั้นสูง AtomicInteger รองรับการทำงานของอะตอมกับตัวแปร int ที่อยู่ภายใต้ มันมีวิธีการรับและตั้งค่าที่ทำงานเหมือนอ่านและเขียนบนตัวแปรระเหย นั่นคือชุดมีความสัมพันธ์ที่เกิดขึ้นก่อนที่จะได้รับตัวแปรเดียวกันในภายหลัง วิธี atomic CompareAndSet ยังมีคุณสมบัติความสอดคล้องของหน่วยความจำเหล่านี้

AtomicInteger Methods

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

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

public int addAndGet(int delta)

เพิ่มค่าที่กำหนดให้กับค่าปัจจุบันในเชิงอะตอม

2

public boolean compareAndSet(int expect, int update)

ตั้งค่าแบบอะตอมเป็นค่าอัพเดตที่กำหนดหากค่าปัจจุบันตรงกับค่าที่คาดไว้

3

public int decrementAndGet()

ลดลงทีละค่าตามค่าปัจจุบัน

4

public double doubleValue()

ส่งคืนค่าของตัวเลขที่ระบุเป็นค่าคู่

5

public float floatValue()

ส่งคืนค่าของตัวเลขที่ระบุเป็นค่าลอย

6

public int get()

รับค่าปัจจุบัน

7

public int getAndAdd(int delta)

Atomiclly เพิ่มค่าที่กำหนดให้กับค่าปัจจุบัน

8

public int getAndDecrement()

ลดลงทีละค่าตามค่าปัจจุบัน

9

public int getAndIncrement()

เพิ่มขึ้นทีละหนึ่งค่าปัจจุบัน

10

public int getAndSet(int newValue)

ตั้งค่าเป็นค่าที่กำหนดโดยอะตอมและส่งกลับค่าเก่า

11

public int incrementAndGet()

เพิ่มขึ้นทีละหนึ่งค่าปัจจุบัน

12

public int intValue()

ส่งกลับค่าของตัวเลขที่ระบุเป็น int

13

public void lazySet(int newValue)

ในที่สุดก็ตั้งค่าเป็นค่าที่กำหนด

14

public long longValue()

ส่งคืนค่าของตัวเลขที่ระบุเป็น long

15

public void set(int newValue)

ตั้งค่าเป็นค่าที่กำหนด

16

public String toString()

ส่งกลับการแสดงสตริงของค่าปัจจุบัน

17

public boolean weakCompareAndSet(int expect, int update)

ตั้งค่าแบบอะตอมเป็นค่าอัพเดตที่กำหนดหากค่าปัจจุบันตรงกับค่าที่คาดไว้

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ตัวนับที่ไม่ปลอดภัยในสภาพแวดล้อมแบบใช้เธรด

public class TestThread {

   static class Counter {
      private int c = 0;

      public void increment() {
         c++;
      }

      public int value() {
         return c;
      }
   }
   
   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {
         
         new Thread(new Runnable() {
            
            public void run() {
               counter.increment();
            }
         }).start(); 
      }  
      Thread.sleep(6000);
      System.out.println("Final number (should be 1000): " + counter.value());
   }  
}

ซึ่งอาจให้ผลลัพธ์ดังต่อไปนี้ขึ้นอยู่กับความเร็วของคอมพิวเตอร์และการแทรกเธรด

เอาต์พุต

Final number (should be 1000): 1000

ตัวอย่าง

import java.util.concurrent.atomic.AtomicInteger;

public class TestThread {

   static class Counter {
      private AtomicInteger c = new AtomicInteger(0);

      public void increment() {
         c.getAndIncrement();
      }

      public int value() {
         return c.get();
      }
   }
   
   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {

         new Thread(new Runnable() {
            public void run() {
               counter.increment();
            }
         }).start(); 
      }  
      Thread.sleep(6000);
      System.out.println("Final number (should be 1000): " + counter.value());
   }
}

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

เอาต์พุต

Final number (should be 1000): 1000

คลาส java.util.concurrent.atomic AtomicLong จัดเตรียมการดำเนินการกับค่ายาวพื้นฐานที่สามารถอ่านและเขียนแบบอะตอมได้และยังมีการดำเนินการของอะตอมขั้นสูง AtomicLong สนับสนุนการดำเนินการของอะตอมกับตัวแปรยาวที่อยู่ภายใต้ มันมีวิธีการรับและตั้งค่าที่ทำงานเหมือนอ่านและเขียนบนตัวแปรระเหย นั่นคือชุดมีความสัมพันธ์ที่เกิดขึ้นก่อนที่จะได้รับตัวแปรเดียวกันในภายหลัง วิธี atomic CompareAndSet ยังมีคุณสมบัติความสอดคล้องของหน่วยความจำเหล่านี้

วิธีการ AtomicLong

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

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

public long addAndGet(long delta)

เพิ่มค่าที่กำหนดให้กับค่าปัจจุบันในเชิงอะตอม

2

public boolean compareAndSet(long expect, long update)

ตั้งค่าแบบอะตอมเป็นค่าอัพเดตที่กำหนดหากค่าปัจจุบันตรงกับค่าที่คาดไว้

3

public long decrementAndGet()

ลดลงทีละค่าตามค่าปัจจุบัน

4

public double doubleValue()

ส่งคืนค่าของตัวเลขที่ระบุเป็นค่าคู่

5

public float floatValue()

ส่งคืนค่าของตัวเลขที่ระบุเป็นค่าลอย

6

public long get()

รับค่าปัจจุบัน

7

public long getAndAdd(long delta)

Atomiclly เพิ่มค่าที่กำหนดให้กับค่าปัจจุบัน

8

public long getAndDecrement()

ลดลงทีละค่าตามค่าปัจจุบัน

9

public long getAndIncrement()

เพิ่มขึ้นทีละหนึ่งค่าปัจจุบัน

10

public long getAndSet(long newValue)

ตั้งค่าเป็นค่าที่กำหนดโดยอะตอมและส่งกลับค่าเก่า

11

public long incrementAndGet()

เพิ่มขึ้นทีละหนึ่งค่าปัจจุบัน

12

public int intValue()

ส่งกลับค่าของตัวเลขที่ระบุเป็น int

13

public void lazySet(long newValue)

ในที่สุดก็ตั้งค่าเป็นค่าที่กำหนด

14

public long longValue()

ส่งคืนค่าของตัวเลขที่ระบุเป็น long

15

public void set(long newValue)

ตั้งค่าเป็นค่าที่กำหนด

16

public String toString()

ส่งกลับการแสดงสตริงของค่าปัจจุบัน

17

public boolean weakCompareAndSet(long expect, long update)

ตั้งค่าแบบอะตอมเป็นค่าอัพเดตที่กำหนดหากค่าปัจจุบันตรงกับค่าที่คาดไว้

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้งานตัวนับอย่างปลอดภัยโดยใช้ AtomicLong ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.atomic.AtomicLong;

public class TestThread {

   static class Counter {
      private AtomicLong c = new AtomicLong(0);

      public void increment() {
         c.getAndIncrement();
      }

      public long value() {
         return c.get();
      }
   }

   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {
         
         new Thread(new Runnable() {
            
            public void run() {
               counter.increment();
            }
         }).start();	
      }
      Thread.sleep(6000);			   		  
      System.out.println("Final number (should be 1000): " + counter.value());
   }
}

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

เอาต์พุต

Final number (should be 1000): 1000

คลาส java.util.concurrent.atomic AtomicBoolean จัดเตรียมการดำเนินการกับค่าบูลีนพื้นฐานที่สามารถอ่านและเขียนแบบอะตอมได้และยังมีการดำเนินการเกี่ยวกับอะตอมขั้นสูง AtomicBoolean รองรับการทำงานของอะตอมกับตัวแปรบูลีนที่อยู่เบื้องหลัง มันมีวิธีการรับและตั้งค่าที่ทำงานเหมือนอ่านและเขียนบนตัวแปรระเหย นั่นคือชุดมีความสัมพันธ์ที่เกิดขึ้นก่อนที่จะได้รับตัวแปรเดียวกันในภายหลัง วิธี atomic CompareAndSet ยังมีคุณสมบัติความสอดคล้องของหน่วยความจำเหล่านี้

วิธี AtomicBoolean

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

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

public boolean compareAndSet(boolean expect, boolean update)

กำหนดค่าด้วยอะตอมเป็นค่าที่อัปเดตที่กำหนดหากค่าปัจจุบัน == ค่าที่คาดหวัง

2

public boolean get()

ส่งคืนค่าปัจจุบัน

3

public boolean getAndSet(boolean newValue)

ตั้งค่าเป็นค่าที่กำหนดโดยอะตอมและส่งกลับค่าก่อนหน้า

4

public void lazySet(boolean newValue)

ในที่สุดก็ตั้งค่าเป็นค่าที่กำหนด

5

public void set(boolean newValue)

ตั้งค่าเป็นค่าที่กำหนดโดยไม่มีเงื่อนไข

6

public String toString()

ส่งกลับการแสดงสตริงของค่าปัจจุบัน

7

public boolean weakCompareAndSet(boolean expect, boolean update)

กำหนดค่าด้วยอะตอมเป็นค่าที่อัปเดตที่กำหนดหากค่าปัจจุบัน == ค่าที่คาดหวัง

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ตัวแปร AtomicBoolean ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.atomic.AtomicBoolean;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final AtomicBoolean atomicBoolean = new AtomicBoolean(false);

      new Thread("Thread 1") {

         public void run() {

            while(true) {
               System.out.println(Thread.currentThread().getName() 
                  +" Waiting for Thread 2 to set Atomic variable to true. Current value is "
                  + atomicBoolean.get());

               if(atomicBoolean.compareAndSet(true, false)) {
                  System.out.println("Done!");
                  break;
               }
            }
         };
      }.start();

      new Thread("Thread 2") {

         public void run() {
            System.out.println(Thread.currentThread().getName() +
               ", Atomic Variable: " +atomicBoolean.get()); 
            System.out.println(Thread.currentThread().getName() +
               " is setting the variable to true ");
            atomicBoolean.set(true);
            System.out.println(Thread.currentThread().getName() +
               ", Atomic Variable: " +atomicBoolean.get()); 
         };
      }.start();
   }
}

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

เอาต์พุต

Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2, Atomic Variable: false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2 is setting the variable to true
Thread 2, Atomic Variable: true
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Done!

คลาส java.util.concurrent.atomic AtomicReference จัดเตรียมการดำเนินการเกี่ยวกับการอ้างอิงอ็อบเจ็กต์พื้นฐานที่สามารถอ่านและเขียนแบบอะตอมได้และยังมีการดำเนินการเกี่ยวกับอะตอมขั้นสูง AtomicReference สนับสนุนการดำเนินการของอะตอมบนตัวแปรอ้างอิงวัตถุ มันมีวิธีการรับและตั้งค่าที่ทำงานเหมือนอ่านและเขียนบนตัวแปรระเหย นั่นคือชุดมีความสัมพันธ์ที่เกิดขึ้นก่อนที่จะได้รับตัวแปรเดียวกันในภายหลัง วิธี atomic CompareAndSet ยังมีคุณสมบัติความสอดคล้องของหน่วยความจำเหล่านี้

AtomicReference Methods

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

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

public boolean compareAndSet(V expect, V update)

กำหนดค่าด้วยอะตอมเป็นค่าที่อัปเดตที่กำหนดหากค่าปัจจุบัน == ค่าที่คาดหวัง

2

public boolean get()

ส่งคืนค่าปัจจุบัน

3

public boolean getAndSet(V newValue)

ตั้งค่าเป็นค่าที่กำหนดโดยอะตอมและส่งกลับค่าก่อนหน้า

4

public void lazySet(V newValue)

ในที่สุดก็ตั้งค่าเป็นค่าที่กำหนด

5

public void set(V newValue)

ตั้งค่าเป็นค่าที่กำหนดโดยไม่มีเงื่อนไข

6

public String toString()

ส่งกลับการแสดงสตริงของค่าปัจจุบัน

7

public boolean weakCompareAndSet(V expect, V update)

กำหนดค่าด้วยอะตอมเป็นค่าที่อัปเดตที่กำหนดหากค่าปัจจุบัน == ค่าที่คาดหวัง

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ตัวแปร AtomicReference ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.atomic.AtomicReference;

public class TestThread {
   private static String message = "hello";
   private static AtomicReference<String> atomicReference;

   public static void main(final String[] arguments) throws InterruptedException {
      atomicReference = new AtomicReference<String>(message);
      
      new Thread("Thread 1") {
         
         public void run() {
            atomicReference.compareAndSet(message, "Thread 1");
            message = message.concat("-Thread 1!");
         };
      }.start();

      System.out.println("Message is: " + message);
      System.out.println("Atomic Reference of Message is: " + atomicReference.get());
   }
}

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

เอาต์พุต

Message is: hello
Atomic Reference of Message is: Thread 1

คลาส java.util.concurrent.atomic AtomicIntegerArray จัดเตรียมการดำเนินการกับอาร์เรย์ int พื้นฐานที่สามารถอ่านและเขียนแบบอะตอมได้และยังมีการดำเนินการเกี่ยวกับอะตอมขั้นสูง AtomicIntegerArray สนับสนุนการทำงานของอะตอมกับตัวแปร int array ที่อยู่ภายใต้ มันมีวิธีการรับและตั้งค่าที่ทำงานเหมือนอ่านและเขียนบนตัวแปรระเหย นั่นคือชุดมีความสัมพันธ์ที่เกิดขึ้นก่อนที่จะได้รับตัวแปรเดียวกันในภายหลัง วิธี atomic CompareAndSet ยังมีคุณสมบัติความสอดคล้องของหน่วยความจำเหล่านี้

AtomicIntegerArray วิธีการ

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

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

public int addAndGet(int i, int delta)

เพิ่มค่าที่กำหนดให้กับองค์ประกอบที่ดัชนี i

2

public boolean compareAndSet(int i, int expect, int update)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดโดยอะตอมหากค่าปัจจุบัน == ค่าที่คาดไว้

3

public int decrementAndGet(int i)

ลดลงทีละองค์ประกอบที่ดัชนี i

4

public int get(int i)

รับค่าปัจจุบันที่ตำแหน่ง i

5

public int getAndAdd(int i, int delta)

เพิ่มค่าที่กำหนดให้กับองค์ประกอบที่ดัชนี i

6

public int getAndDecrement(int i)

ลดลงทีละองค์ประกอบที่ดัชนี i

7

public int getAndIncrement(int i)

เพิ่มขึ้นทีละองค์ประกอบที่ดัชนี i

8

public int getAndSet(int i, int newValue)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดและส่งคืนค่าเก่า

9

public int incrementAndGet(int i)

เพิ่มขึ้นทีละองค์ประกอบที่ดัชนี i

10

public void lazySet(int i, int newValue)

ในที่สุดตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนด

11

public int length()

ส่งกลับความยาวของอาร์เรย์

12

public void set(int i, int newValue)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนด

13

public String toString()

ส่งกลับการแสดงสตริงของค่าปัจจุบันของอาร์เรย์

14

public boolean weakCompareAndSet(int i, int expect, int update)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดโดยอะตอมหากค่าปัจจุบัน == ค่าที่คาดไว้

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ตัวแปร AtomicIntegerArray ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.atomic.AtomicIntegerArray;

public class TestThread {
   private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);

   public static void main(final String[] arguments) throws InterruptedException {
      
      for (int i = 0; i<atomicIntegerArray.length(); i++) {
         atomicIntegerArray.set(i, 1);
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();

      System.out.println("Values: ");

      for (int i = 0; i<atomicIntegerArray.length(); i++) {
         System.out.print(atomicIntegerArray.get(i) + " ");
      }
   }

   static class Increment implements Runnable {

      public void run() {

         for(int i = 0; i<atomicIntegerArray.length(); i++) {
            int add = atomicIntegerArray.incrementAndGet(i);
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {

      public void run() {

         for(int i = 0; i<atomicIntegerArray.length(); i++) {
            boolean swapped = atomicIntegerArray.compareAndSet(i, 2, 3);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId()
                  + ", index " +i + ", value: 3");
            }
         }
      }
   }
}

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

เอาต์พุต

Thread 10, index 0, value: 2
Thread 10, index 1, value: 2
Thread 10, index 2, value: 2
Thread 11, index 0, value: 3
Thread 10, index 3, value: 2
Thread 11, index 1, value: 3
Thread 11, index 2, value: 3
Thread 10, index 4, value: 2
Thread 11, index 3, value: 3
Thread 10, index 5, value: 2
Thread 10, index 6, value: 2
Thread 11, index 4, value: 3
Thread 10, index 7, value: 2
Thread 11, index 5, value: 3
Thread 10, index 8, value: 2
Thread 11, index 6, value: 3
Thread 10, index 9, value: 2
Thread 11, index 7, value: 3
Thread 11, index 8, value: 3
Thread 11, index 9, value: 3
Values:
3 3 3 3 3 3 3 3 3 3

คลาส java.util.concurrent.atomic AtomicLongArray จัดเตรียมการดำเนินการกับอาร์เรย์แบบยาวที่อยู่ภายใต้ซึ่งสามารถอ่านและเขียนแบบอะตอมได้และยังมีการดำเนินการเกี่ยวกับอะตอมขั้นสูง AtomicLongArray สนับสนุนการทำงานของอะตอมกับตัวแปรอาร์เรย์แบบยาวที่อยู่ภายใต้ มันมีวิธีการรับและตั้งค่าที่ทำงานเหมือนอ่านและเขียนบนตัวแปรระเหย นั่นคือชุดมีความสัมพันธ์ที่เกิดขึ้นก่อนที่จะได้รับตัวแปรเดียวกันในภายหลัง วิธี atomic CompareAndSet ยังมีคุณสมบัติความสอดคล้องของหน่วยความจำเหล่านี้

วิธี AtomicLongArray

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

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

public long addAndGet(int i, long delta)

เพิ่มค่าที่กำหนดให้กับองค์ประกอบที่ดัชนี i

2

public boolean compareAndSet(int i, long expect, long update)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดโดยอะตอมหากค่าปัจจุบัน == ค่าที่คาดไว้

3

public long decrementAndGet(int i)

ลดลงทีละองค์ประกอบที่ดัชนี i

4

public long get(int i)

รับค่าปัจจุบันที่ตำแหน่ง i

5

public long getAndAdd(int i, long delta)

เพิ่มค่าที่กำหนดให้กับองค์ประกอบที่ดัชนี i

6

public long getAndDecrement(int i)

ลดลงทีละองค์ประกอบที่ดัชนี i

7

public long getAndIncrement(int i)

เพิ่มขึ้นทีละองค์ประกอบที่ดัชนี i

8

public long getAndSet(int i, long newValue)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดและส่งคืนค่าเก่า

9

public long incrementAndGet(int i)

เพิ่มขึ้นทีละองค์ประกอบที่ดัชนี i

10

public void lazySet(int i, long newValue)

ในที่สุดตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนด

11

public int length()

ส่งกลับความยาวของอาร์เรย์

12

public void set(int i, long newValue)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนด

13

public String toString()

ส่งกลับการแสดงสตริงของค่าปัจจุบันของอาร์เรย์

14

public boolean weakCompareAndSet(int i, long expect, long update)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดโดยอะตอมหากค่าปัจจุบัน == ค่าที่คาดไว้

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ตัวแปร AtomicIntegerArray ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.atomic.AtomicLongArray;

public class TestThread {
   private static AtomicLongArray atomicLongArray = new AtomicLongArray(10);

   public static void main(final String[] arguments) throws InterruptedException {

      for (int i = 0; i<atomicLongArray.length(); i++) {
         atomicLongArray.set(i, 1);
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();

      System.out.println("Values: ");
      
      for (int i = 0; i<atomicLongArray.length(); i++) {
         System.out.print(atomicLongArray.get(i) + " ");
      }
   }  

   static class Increment implements Runnable {

      public void run() {

         for(int i = 0; i<atomicLongArray.length(); i++) {
            long add = atomicLongArray.incrementAndGet(i);
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {

      public void run() {

         for(int i = 0; i<atomicLongArray.length(); i++) {
            boolean swapped = atomicLongArray.compareAndSet(i, 2, 3);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId()
                  + ", index " +i + ", value: 3");
            }
         }
      }
   }
}

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

เอาต์พุต

Thread 9, index 0, value: 2
Thread 10, index 0, value: 3
Thread 9, index 1, value: 2
Thread 9, index 2, value: 2
Thread 9, index 3, value: 2
Thread 9, index 4, value: 2
Thread 10, index 1, value: 3
Thread 9, index 5, value: 2
Thread 10, index 2, value: 3
Thread 9, index 6, value: 2
Thread 10, index 3, value: 3
Thread 9, index 7, value: 2
Thread 10, index 4, value: 3
Thread 9, index 8, value: 2
Thread 9, index 9, value: 2
Thread 10, index 5, value: 3
Thread 10, index 6, value: 3
Thread 10, index 7, value: 3
Thread 10, index 8, value: 3
Thread 10, index 9, value: 3
Values: 
3 3 3 3 3 3 3 3 3 3

คลาส java.util.concurrent.atomic AtomicReferenceArray จัดเตรียมการดำเนินการกับอาร์เรย์อ้างอิงพื้นฐานที่สามารถอ่านและเขียนแบบอะตอมได้และยังมีการดำเนินการเกี่ยวกับอะตอมขั้นสูง AtomicReferenceArray สนับสนุนการดำเนินการของอะตอมบนตัวแปรอาร์เรย์อ้างอิงที่อ้างอิง มันมีวิธีการรับและตั้งค่าที่ทำงานเหมือนอ่านและเขียนบนตัวแปรระเหย นั่นคือชุดมีความสัมพันธ์ที่เกิดขึ้นก่อนที่จะได้รับตัวแปรเดียวกันในภายหลัง วิธี atomic CompareAndSet ยังมีคุณสมบัติความสอดคล้องของหน่วยความจำเหล่านี้

AtomicReferenceArray Methods

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

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

public boolean compareAndSet(int i, E expect, E update)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดโดยอะตอมหากค่าปัจจุบัน == ค่าที่คาดไว้

2

public E get(int i)

รับค่าปัจจุบันที่ตำแหน่ง i

3

public E getAndSet(int i, E newValue)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดและส่งคืนค่าเก่า

4

public void lazySet(int i, E newValue)

ในที่สุดตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนด

5

public int length()

ส่งกลับความยาวของอาร์เรย์

6

public void set(int i, E newValue)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนด

7

public String toString()

ส่งกลับการแสดงสตริงของค่าปัจจุบันของอาร์เรย์

8

public boolean weakCompareAndSet(int i, E expect, E update)

ตั้งค่าองค์ประกอบที่ตำแหน่ง i เป็นค่าที่กำหนดโดยอะตอมหากค่าปัจจุบัน == ค่าที่คาดไว้

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ตัวแปร AtomicReferenceArray ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.atomic.AtomicReferenceArray;

public class TestThread {
   private static String[] source = new String[10];
   private static AtomicReferenceArray<String> atomicReferenceArray 
      = new AtomicReferenceArray<String>(source);

   public static void main(final String[] arguments) throws InterruptedException {

      for (int i = 0; i<atomicReferenceArray.length(); i++) {
         atomicReferenceArray.set(i, "item-2");
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();		
   }  

   static class Increment implements Runnable {
      
      public void run() {
         
         for(int i = 0; i<atomicReferenceArray.length(); i++) {
            String add = atomicReferenceArray.getAndSet(i,"item-"+ (i+1));
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {
      
      public void run() {
         
         for(int i = 0; i<atomicReferenceArray.length(); i++) {
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ atomicReferenceArray.get(i));
            boolean swapped = atomicReferenceArray.compareAndSet(i, "item-2", "updated-item-2");
            System.out.println("Item swapped: " + swapped);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId() 
                  + ", index " +i + ", updated-item-2");
            }
         }
      }
   }
}

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

เอาต์พุต

Thread 9, index 0, value: item-2
Thread 10, index 0, value: item-1
Item swapped: false
Thread 10, index 1, value: item-2
Item swapped: true
Thread 9, index 1, value: updated-item-2
Thread 10, index 1, updated-item-2
Thread 10, index 2, value: item-3
Item swapped: false
Thread 10, index 3, value: item-2
Item swapped: true
Thread 10, index 3, updated-item-2
Thread 10, index 4, value: item-2
Item swapped: true
Thread 10, index 4, updated-item-2
Thread 10, index 5, value: item-2
Item swapped: true
Thread 10, index 5, updated-item-2
Thread 10, index 6, value: item-2
Thread 9, index 2, value: item-2
Item swapped: true
Thread 9, index 3, value: updated-item-2
Thread 10, index 6, updated-item-2
Thread 10, index 7, value: item-2
Thread 9, index 4, value: updated-item-2
Item swapped: true
Thread 9, index 5, value: updated-item-2
Thread 10, index 7, updated-item-2
Thread 9, index 6, value: updated-item-2
Thread 10, index 8, value: item-2
Thread 9, index 7, value: updated-item-2
Item swapped: true
Thread 9, index 8, value: updated-item-2
Thread 10, index 8, updated-item-2
Thread 9, index 9, value: item-2
Thread 10, index 9, value: item-10
Item swapped: false

อินเทอร์เฟซ java.util.concurrent.Executor เป็นอินเทอร์เฟซที่เรียบง่ายเพื่อรองรับการเรียกใช้งานใหม่

วิธีการให้บริการ

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

void execute(Runnable command)

เรียกใช้คำสั่งที่กำหนดในบางครั้งในอนาคต

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้อินเตอร์เฟส Executor ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      Executor executor = Executors.newCachedThreadPool();
      executor.execute(new Task());
      ThreadPoolExecutor pool = (ThreadPoolExecutor)executor;
      pool.shutdown();
   }  

   static class Task implements Runnable {
      
      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed");
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

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

เอาต์พุต

Running Task!
Task Completed

อินเทอร์เฟซ java.util.concurrent.ExecutorService เป็นอินเทอร์เฟซย่อยของอินเทอร์เฟซ Executor และเพิ่มคุณสมบัติในการจัดการวงจรชีวิตทั้งของแต่ละงานและของตัวดำเนินการเอง

วิธีการให้บริการ

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

boolean awaitTermination(long timeout, TimeUnit unit)

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

2

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)

ดำเนินงานที่กำหนดส่งคืนรายการ Futures ที่มีสถานะและผลลัพธ์เมื่อทั้งหมดเสร็จสมบูรณ์

3

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

ดำเนินงานที่กำหนดส่งคืนรายการ Futures ที่มีสถานะและผลลัพธ์เมื่อทั้งหมดเสร็จสมบูรณ์หรือหมดเวลาหมดเวลาแล้วแต่ว่าสิ่งใดจะเกิดขึ้นก่อน

4

<T> T invokeAny(Collection<? extends Callable<T>> tasks)

ดำเนินการงานที่กำหนดส่งคืนผลลัพธ์ของงานที่ทำสำเร็จ (กล่าวคือโดยไม่มีข้อยกเว้น) ถ้ามี

5

<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

ดำเนินการงานที่กำหนดส่งคืนผลลัพธ์ของงานที่ทำสำเร็จ (กล่าวคือโดยไม่ทิ้งข้อยกเว้น) หากมีการดำเนินการก่อนที่จะหมดเวลาที่กำหนด
6

boolean isShutdown()

คืนค่าจริงหากตัวดำเนินการนี้ถูกปิด

7

boolean isTerminated()

ส่งคืนค่าจริงหากงานทั้งหมดเสร็จสิ้นหลังจากปิดเครื่อง

8

void shutdown()

เริ่มต้นการปิดระบบอย่างเป็นระเบียบซึ่งมีการดำเนินงานที่ส่งไปก่อนหน้านี้ แต่จะไม่มีการยอมรับงานใหม่

9

List<Runnable> shutdownNow()

พยายามหยุดงานที่กำลังดำเนินการอยู่ทั้งหมดหยุดการประมวลผลงานที่รอและส่งคืนรายการของงานที่รอการดำเนินการ

10

<T> Future<T> submit(Callable<T> task)

ส่งงานคืนค่าสำหรับการดำเนินการและส่งคืนอนาคตที่แสดงถึงผลลัพธ์ที่รอดำเนินการของงาน

11

Future<?> submit(Runnable task)

ส่งงานที่รันได้เพื่อดำเนินการและส่งคืนอนาคตที่เป็นตัวแทนของงานนั้น

12

<T> Future<T> submit(Runnable task, T result)

ส่งงานที่รันได้เพื่อดำเนินการและส่งคืนอนาคตที่เป็นตัวแทนของงานนั้น

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้อินเตอร์เฟส ExecutorService ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newSingleThreadExecutor();

      try {
         executor.submit(new Task());
         System.out.println("Shutdown executor");
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         System.err.println("tasks interrupted");
      } finally {

         if (!executor.isTerminated()) {
            System.err.println("cancel non-finished tasks");
         }
         executor.shutdownNow();
         System.out.println("shutdown finished");
      }
   }

   static class Task implements Runnable {
      
      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 20);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }	   
}

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

เอาต์พุต

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:302)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
	at TestThread$Task.run(TestThread.java:39)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

อินเทอร์เฟซ java.util.concurrent.ScheduledExecutorService เป็นอินเทอร์เฟซย่อยของอินเตอร์เฟส ExecutorService และรองรับการดำเนินการงานในอนาคตและ / หรือเป็นระยะ

วิธีการบริการตามกำหนดการ

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

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

สร้างและเรียกใช้งาน ScheduledFuture ที่เปิดใช้งานหลังจากความล่าช้าที่กำหนด

2

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

สร้างและดำเนินการแอ็คชั่น one-shot ที่เปิดใช้งานหลังจากดีเลย์ที่กำหนด

3

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

สร้างและดำเนินการตามระยะเวลาที่เปิดใช้งานก่อนหลังจากการหน่วงเวลาเริ่มต้นที่กำหนดและตามมาด้วยช่วงเวลาที่กำหนด นั่นคือการดำเนินการจะเริ่มหลังจาก initialDelay แล้ว initialDelay + period จากนั้น initialDelay + 2 * period และอื่น ๆ

4

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

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

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้อินเทอร์เฟซ ScheduledExecutorService ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }

   static class BeepTask implements Runnable {
      
      public void run() {
         System.out.println("beep");      
      }
   }
}

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

เอาต์พุต

beep
beep
beep
beep

สามารถรับพูลเธรดคงที่ได้โดยเรียกเมธอด newFixedThreadPool () แบบคงที่ของคลาส Executors

ไวยากรณ์

ExecutorService fixedPool = Executors.newFixedThreadPool(2);

ที่ไหน

  • เธรดสูงสุด 2 เธรดจะแอ็คทีฟเพื่อประมวลผลงาน

  • หากมีการส่งมากกว่า 2 เธรดเธรดนั้นจะถูกกักไว้ในคิวจนกว่าเธรดจะพร้อมใช้งาน

  • เธรดใหม่ถูกสร้างขึ้นเพื่อใช้แทนหากเธรดสิ้นสุดลงเนื่องจากความล้มเหลวในระหว่างการปิดการดำเนินการบนตัวเรียกใช้งาน

  • เธรดใด ๆ ที่มีอยู่จนกว่าพูลจะปิดลง

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้เมธอด newFixedThreadPool ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newFixedThreadPool(2);

      // Cast the object to its class type
      ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + pool.getCorePoolSize());
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
               TimeUnit.SECONDS.sleep(duration);
            
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

This will produce the following result.

Output

Largest executions: 0
Maximum allowed threads: 2
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 2
Largest executions: 2
Maximum allowed threads: 2
Current threads in pool: 2
Currently executing threads: 1
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1

A cached thread pool can be obtainted by calling the static newCachedThreadPool() method of Executors class.

Syntax

ExecutorService executor = Executors.newCachedThreadPool();

where

  • newCachedThreadPool method creates an executor having an expandable thread pool.

  • Such an executor is suitable for applications that launch many short-lived tasks.

Example

The following TestThread program shows usage of newCachedThreadPool method in thread based environment.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newCachedThreadPool();

      // Cast the object to its class type
      ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + pool.getCorePoolSize());
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
               TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

This will produce the following result.

Output

Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1

A scheduled thread pool can be obtainted by calling the static newScheduledThreadPool() method of Executors class.

Syntax

ExecutorService executor = Executors.newScheduledThreadPool(1);

Example

The following TestThread program shows usage of newScheduledThreadPool method in thread based environment.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }  

   static class BeepTask implements Runnable {

      public void run() {
         System.out.println("beep");      
      }
   }
}

This will produce the following result.

Output

beep
beep
beep
beep

A single thread pool can be obtainted by calling the static newSingleThreadExecutor() method of Executors class.

Syntax

ExecutorService executor = Executors.newSingleThreadExecutor();

Where newSingleThreadExecutor method creates an executor that executes a single task at a time.

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้เมธอด newSingleThreadExecutor ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newSingleThreadExecutor();

      try {
         executor.submit(new Task());
         System.out.println("Shutdown executor");
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         System.err.println("tasks interrupted");
      } finally {

         if (!executor.isTerminated()) {
            System.err.println("cancel non-finished tasks");
         }
         executor.shutdownNow();
         System.out.println("shutdown finished");
      }
   }

   static class Task implements Runnable {
      
      public void run() {

         try {
            Long duration = (long) (Math.random() * 20);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

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

เอาต์พุต

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:302)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
	at TestThread$Task.run(TestThread.java:39) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

java.util.concurrent.ThreadPoolExecutor เป็น ExecutorService เพื่อดำเนินการแต่ละงานที่ส่งโดยใช้เธรดที่รวมกันหลายเธรดซึ่งปกติจะกำหนดค่าโดยใช้เมธอด Executors factory นอกจากนี้ยังมีวิธีการยูทิลิตี้ต่างๆเพื่อตรวจสอบสถิติเธรดปัจจุบันและควบคุม

วิธี ThreadPoolExecutor

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

protected void afterExecute(Runnable r, Throwable t)

วิธีที่เรียกใช้เมื่อเสร็จสิ้นการเรียกใช้ Runnable ที่กำหนด

2

void allowCoreThreadTimeOut(boolean value)

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

3

boolean allowsCoreThreadTimeOut()

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

4

boolean awaitTermination(long timeout, TimeUnit unit)

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

5

protected void beforeExecute(Thread t, Runnable r)

วิธีที่เรียกใช้ก่อนที่จะดำเนินการ Runnable ที่กำหนดในเธรดที่กำหนด

6

void execute(Runnable command)

ดำเนินการงานที่กำหนดในอนาคต

7

protected void finalize()

เรียกใช้การปิดระบบเมื่อตัวดำเนินการนี้ไม่ได้อ้างอิงอีกต่อไปและไม่มีเธรด

8

int getActiveCount()

ส่งคืนจำนวนเธรดโดยประมาณที่กำลังรันงานอยู่

9

long getCompletedTaskCount()

ส่งคืนจำนวนงานทั้งหมดโดยประมาณที่ดำเนินการเสร็จสิ้น

10

int getCorePoolSize()

ส่งคืนจำนวนแกนของเธรด

11

long getKeepAliveTime(TimeUnit unit)

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

12

int getLargestPoolSize()

ส่งคืนจำนวนเธรดจำนวนมากที่สุดที่เคยอยู่ในพูลพร้อมกัน

13

int getMaximumPoolSize()

ส่งคืนจำนวนเธรดสูงสุดที่อนุญาต

14

int getPoolSize()

ส่งคืนจำนวนเธรดปัจจุบันในพูล

15

BlockingQueue getQueue()

ส่งคืนคิวงานที่ใช้โดยตัวดำเนินการนี้

15

RejectedExecutionHandler getRejectedExecutionHandler()

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

16

long getTaskCount()

ส่งคืนจำนวนงานทั้งหมดโดยประมาณที่เคยกำหนดไว้สำหรับการดำเนินการ

17

ThreadFactory getThreadFactory()

ส่งคืนโรงงานเธรดที่ใช้ในการสร้างเธรดใหม่

18

boolean isShutdown()

คืนค่าจริงหากตัวดำเนินการนี้ถูกปิด

19

boolean isTerminated()

ส่งคืนค่าจริงหากงานทั้งหมดเสร็จสิ้นหลังจากปิดเครื่อง

20

boolean isTerminating()

ส่งคืนค่าจริงหากตัวดำเนินการนี้อยู่ระหว่างการยุติหลังจากปิดระบบ () หรือ shutdownNow () แต่ยังไม่สิ้นสุด

21

int prestartAllCoreThreads()

เริ่มต้นเธรดหลักทั้งหมดทำให้ไม่ต้องรอการทำงาน

22

boolean prestartCoreThread()

เริ่มเธรดหลักทำให้รอการทำงาน

23

void purge()

พยายามลบออกจากคิวงานงานในอนาคตทั้งหมดที่ถูกยกเลิก

24

boolean remove(Runnable task)

ลบงานนี้ออกจากคิวภายในของตัวดำเนินการหากมีอยู่ดังนั้นจึงไม่สามารถรันได้หากยังไม่ได้เริ่มทำงาน

25

void setCorePoolSize(int corePoolSize)

ตั้งค่าจำนวนแกนของเธรด

26

void setKeepAliveTime(long time, TimeUnit unit)

ตั้งค่าการ จำกัด เวลาที่เธรดอาจไม่ได้ใช้งานก่อนที่จะยุติ

27

void setMaximumPoolSize(int maximumPoolSize)

ตั้งค่าจำนวนเธรดสูงสุดที่อนุญาต

28

void setRejectedExecutionHandler(RejectedExecutionHandler handler)

ตั้งค่าตัวจัดการใหม่สำหรับงานที่ไม่สามารถดำเนินการได้

29

void setThreadFactory(ThreadFactory threadFactory)

ตั้งค่าโรงงานเธรดที่ใช้สร้างเธรดใหม่

30

void shutdown()

เริ่มต้นการปิดระบบอย่างเป็นระเบียบซึ่งมีการดำเนินงานที่ส่งไปก่อนหน้านี้ แต่จะไม่มีการยอมรับงานใหม่

31

List<Runnable> shutdownNow()

พยายามหยุดงานที่กำลังดำเนินการอยู่ทั้งหมดหยุดการประมวลผลงานที่รอและส่งคืนรายการของงานที่รอการดำเนินการ

32

protected void terminated()

เรียกเมธอดเมื่อผู้ดำเนินการยกเลิก

33

String toString()

ส่งคืนสตริงที่ระบุพูลนี้ตลอดจนสถานะรวมถึงการบ่งชี้สถานะการรันและจำนวนผู้ปฏิบัติงานและงานโดยประมาณ

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้อินเตอร์เฟส ThreadPoolExecutor ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newCachedThreadPool();

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + executor.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + executor.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + executor.getPoolSize());
      System.out.println("Currently executing threads: "
         + executor.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + executor.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + executor.getCorePoolSize());
      System.out.println("Largest executions: "
         + executor.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + executor.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + executor.getPoolSize());
      System.out.println("Currently executing threads: "
         + executor.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + executor.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {

         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

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

เอาต์พุต

Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-2
Running Task! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-2

java.util.concurrent.ScheduledThreadPoolExecutor เป็นคลาสย่อยของ ThreadPoolExecutor และสามารถกำหนดเวลาคำสั่งเพิ่มเติมให้ทำงานหลังจากการหน่วงเวลาที่กำหนดหรือเพื่อดำเนินการเป็นระยะ ๆ

เมธอด ScheduledThreadPoolExecutor

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

protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task)

แก้ไขหรือแทนที่งานที่ใช้ในการเรียกใช้งาน

2

protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task)

แก้ไขหรือแทนที่งานที่ใช้ในการรันรันได้

3

void execute(Runnable command)

ดำเนินการคำสั่งโดยมีการหน่วงเวลาที่ต้องการเป็นศูนย์

4

boolean getContinueExistingPeriodicTasksAfterShutdownPolicy()

รับนโยบายว่าจะดำเนินการงานประจำงวดที่มีอยู่ต่อไปหรือไม่แม้ว่าตัวดำเนินการนี้จะถูกปิด

5

boolean getExecuteExistingDelayedTasksAfterShutdownPolicy()

รับนโยบายว่าจะดำเนินการงานล่าช้าที่มีอยู่หรือไม่แม้ว่าตัวดำเนินการนี้จะถูกปิด

6

BlockingQueue<Runnable> getQueue()

ส่งคืนคิวงานที่ใช้โดยตัวดำเนินการนี้

7

boolean getRemoveOnCancelPolicy()

รับนโยบายว่าควรนำงานที่ยกเลิกออกจากคิวงานทันทีเมื่อยกเลิกหรือไม่

8

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

สร้างและเรียกใช้งาน ScheduledFuture ที่เปิดใช้งานหลังจากความล่าช้าที่กำหนด

9

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

สร้างและดำเนินการแอ็คชั่น one-shot ที่เปิดใช้งานหลังจากดีเลย์ที่กำหนด

10

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

สร้างและดำเนินการตามระยะเวลาที่เปิดใช้งานก่อนหลังจากการหน่วงเวลาเริ่มต้นที่กำหนดและตามมาด้วยช่วงเวลาที่กำหนด นั่นคือการดำเนินการจะเริ่มหลังจาก initialDelay แล้ว initialDelay + period จากนั้น initialDelay + 2 * period และอื่น ๆ

11

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

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

12

void setContinueExistingPeriodicTasksAfterShutdownPolicy (boolean value)

ตั้งค่านโยบายว่าจะดำเนินการงานประจำงวดที่มีอยู่ต่อไปหรือไม่แม้ว่าตัวดำเนินการนี้จะถูกปิด

13

void setExecuteExistingDelayedTasksAfterShutdownPolicy (boolean value)

ตั้งค่านโยบายว่าจะดำเนินการงานล่าช้าที่มีอยู่หรือไม่แม้ว่าตัวดำเนินการนี้จะถูกปิด

14

void setRemoveOnCancelPolicy(boolean value)

กำหนดนโยบายว่าควรนำงานที่ยกเลิกออกจากคิวงานทันทีหรือไม่ในเวลาที่ยกเลิก

15

void shutdown()

เริ่มต้นการปิดระบบอย่างเป็นระเบียบซึ่งมีการดำเนินงานที่ส่งไปก่อนหน้านี้ แต่จะไม่มีการยอมรับงานใหม่

16

List<Runnable> shutdownNow()

พยายามหยุดงานที่กำลังดำเนินการอยู่ทั้งหมดหยุดการประมวลผลงานที่รอและส่งคืนรายการของงานที่รอการดำเนินการ

17

<T> Future<T> submit(Callable<T> task)

ส่งงานคืนค่าสำหรับการดำเนินการและส่งคืนอนาคตที่แสดงถึงผลลัพธ์ที่รอดำเนินการของงาน

18

Future<?> submit(Runnable task)

ส่งงานที่รันได้เพื่อดำเนินการและส่งคืนอนาคตที่เป็นตัวแทนของงานนั้น

19

<T> Future<T> submit(Runnable task, T result)

ส่งงานที่รันได้เพื่อดำเนินการและส่งคืนอนาคตที่เป็นตัวแทนของงานนั้น

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้อินเทอร์เฟซ ScheduledThreadPoolExecutor ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledThreadPoolExecutor scheduler = 
         (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }  

   static class BeepTask implements Runnable {
      
      public void run() {
         System.out.println("beep");      
      }
   }
}

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

เอาต์พุต

beep
beep
beep
beep

java.util.concurrent วัตถุที่เรียกได้สามารถส่งคืนผลลัพธ์ที่คำนวณโดยเธรดตรงกันข้ามกับอินเทอร์เฟซที่รันได้ซึ่งสามารถรันเธรดได้เท่านั้น อ็อบเจ็กต์ที่เรียกได้จะส่งคืนอ็อบเจ็กต์ในอนาคตซึ่งจัดเตรียมเมธอดในการตรวจสอบความคืบหน้าของงานที่ดำเนินการโดยเธรด สามารถใช้อ็อบเจ็กต์ในอนาคตเพื่อตรวจสอบสถานะของ Callable จากนั้นดึงผลลัพธ์จาก Callable เมื่อเธรดเสร็จสิ้น นอกจากนี้ยังมีฟังก์ชันการหมดเวลา

ไวยากรณ์

//submit the callable using ThreadExecutor
//and get the result as a Future object
Future<Long> result10 = executor.submit(new FactorialService(10));
 
//get the result using get method of the Future object
//get method waits till the thread execution and then return the result of the execution.
Long factorial10 = result10.get();

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ Futures และ Callables ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException,
      ExecutionException {

      ExecutorService executor = Executors.newSingleThreadExecutor();

      System.out.println("Factorial Service called for 10!");
      Future<Long> result10 = executor.submit(new FactorialService(10));

      System.out.println("Factorial Service called for 20!");
      Future<Long> result20 = executor.submit(new FactorialService(20));

      Long factorial10 = result10.get();
      System.out.println("10! = " + factorial10);

      Long factorial20 = result20.get();
      System.out.println("20! = " + factorial20);

      executor.shutdown();
   }  

   static class FactorialService implements Callable<Long> {
      private int number;

      public FactorialService(int number) {
         this.number = number;
      }

      @Override
      public Long call() throws Exception {
         return factorial();
      }

      private Long factorial() throws InterruptedException {
         long result = 1; 
         
         while (number != 0) { 
            result = number * result; 
            number--; 
            Thread.sleep(100); 
         }
         return result;	
      }
   }
}

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

เอาต์พุต

Factorial Service called for 10!
Factorial Service called for 20!
10! = 3628800
20! = 2432902008176640000

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

ส้อม

ส้อมเป็นกระบวนการที่งานแยกตัวเองออกเป็นงานย่อยที่เล็กกว่าและแยกจากกันซึ่งสามารถดำเนินการไปพร้อมกันได้

ไวยากรณ์

Sum left  = new Sum(array, low, mid);
left.fork();

นี่คือ Sum เป็นคลาสย่อยของ RecursiveTask และ left.fork () แยกงานออกเป็นงานย่อย

เข้าร่วม

การเข้าร่วมเป็นกระบวนการที่งานเข้าร่วมผลลัพธ์ของงานย่อยทั้งหมดเมื่องานย่อยเสร็จสิ้นการดำเนินการมิฉะนั้นจะรอต่อไป

ไวยากรณ์

left.join();

ด้านซ้ายเป็นวัตถุของคลาส Sum

ForkJoinPool

เป็นเธรดพูลพิเศษที่ออกแบบมาเพื่อทำงานร่วมกับการแยกงานแบบแยกส่วนและเข้าร่วม

ไวยากรณ์

ForkJoinPool forkJoinPool = new ForkJoinPool(4);

นี่คือ ForkJoinPool ใหม่ที่มีระดับซีพียู 4 ตัวแบบขนาน

RecursiveAction

RecursiveAction แสดงถึงงานที่ไม่ส่งคืนค่าใด ๆ

ไวยากรณ์

class Writer extends RecursiveAction {
   @Override
   protected void compute() { }
}

RecursiveTask

RecursiveTask แสดงถึงงานที่ส่งคืนค่า

ไวยากรณ์

class Sum extends RecursiveTask<Long> {
   @Override
   protected Long compute() { return null; }
}

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้กรอบงาน Fork-Join ในสภาพแวดล้อมแบบเธรด

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException, 
      ExecutionException {
      
      int nThreads = Runtime.getRuntime().availableProcessors();
      System.out.println(nThreads);
      
      int[] numbers = new int[1000]; 

      for(int i = 0; i < numbers.length; i++) {
         numbers[i] = i;
      }

      ForkJoinPool forkJoinPool = new ForkJoinPool(nThreads);
      Long result = forkJoinPool.invoke(new Sum(numbers,0,numbers.length));
      System.out.println(result);
   }  

   static class Sum extends RecursiveTask<Long> {
      int low;
      int high;
      int[] array;

      Sum(int[] array, int low, int high) {
         this.array = array;
         this.low   = low;
         this.high  = high;
      }

      protected Long compute() {
         
         if(high - low <= 10) {
            long sum = 0;
            
            for(int i = low; i < high; ++i) 
               sum += array[i];
               return sum;
         } else {	    	
            int mid = low + (high - low) / 2;
            Sum left  = new Sum(array, low, mid);
            Sum right = new Sum(array, mid, high);
            left.fork();
            long rightResult = right.compute();
            long leftResult  = left.join();
            return leftResult + rightResult;
         }
      }
   }
}

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

เอาต์พุต

32
499500

อินเทอร์เฟซ java.util.concurrent.BlockingQueue เป็นอินเทอร์เฟซย่อยของอินเทอร์เฟซคิวและสนับสนุนการดำเนินการเพิ่มเติมเช่นการรอให้คิวไม่ว่างเปล่าก่อนที่จะดึงองค์ประกอบและรอให้มีพื้นที่ว่างในคิวก่อนที่จะจัดเก็บองค์ประกอบ .

วิธีการบล็อกคิว

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

boolean add(E e)

แทรกองค์ประกอบที่ระบุลงในคิวนี้หากสามารถทำได้ทันทีโดยไม่ละเมิดข้อ จำกัด ด้านความจุคืนค่าจริงเมื่อสำเร็จและโยน IllegalStateException หากไม่มีพื้นที่ว่างในขณะนี้

2

boolean contains(Object o)

ส่งคืนจริงหากคิวนี้มีองค์ประกอบที่ระบุ

3

int drainTo(Collection<? super E> c)

ลบองค์ประกอบที่มีอยู่ทั้งหมดออกจากคิวนี้และเพิ่มลงในคอลเลกชันที่กำหนด

4

int drainTo(Collection<? super E> c, int maxElements)

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

5

boolean offer(E e)

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

6

boolean offer(E e, long timeout, TimeUnit unit)

แทรกองค์ประกอบที่ระบุลงในคิวนี้รอจนถึงเวลารอที่ระบุหากจำเป็นเพื่อให้มีพื้นที่ว่าง

7

E poll(long timeout, TimeUnit unit)

ดึงและลบส่วนหัวของคิวนี้รอจนถึงเวลารอที่ระบุหากจำเป็นเพื่อให้องค์ประกอบพร้อมใช้งาน

8

void put(E e)

แทรกองค์ประกอบที่ระบุลงในคิวนี้รอหากจำเป็นเพื่อให้มีพื้นที่ว่าง

9

int remainingCapacity()

ส่งคืนจำนวนขององค์ประกอบเพิ่มเติมที่คิวนี้สามารถตามสมควร (ในกรณีที่ไม่มีข้อ จำกัด ของหน่วยความจำหรือทรัพยากร) ยอมรับโดยไม่มีการบล็อกหรือ Integer.MAX_VALUE หากไม่มีขีด จำกัด ภายใน

10

boolean remove(Object o)

ลบอินสแตนซ์เดียวขององค์ประกอบที่ระบุออกจากคิวนี้หากมีอยู่

11

E take()

ดึงและลบส่วนหัวของคิวนี้รอถ้าจำเป็นจนกว่าองค์ประกอบจะพร้อมใช้งาน

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้อินเตอร์เฟส BlockingQueue ในสภาพแวดล้อมแบบเธรด

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);

      Producer producer = new Producer(queue);
      Consumer consumer = new Consumer(queue);

      new Thread(producer).start();
      new Thread(consumer).start();

      Thread.sleep(4000);
   }  


   static class Producer implements Runnable {
      private BlockingQueue<Integer> queue;

      public Producer(BlockingQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         Random random = new Random();

         try {
            int result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
            
            result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
            
            result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }	   
   }

   static class Consumer implements Runnable {
      private BlockingQueue<Integer> queue;

      public Consumer(BlockingQueue queue) {
         this.queue = queue;
      }
      
      @Override
      public void run() {
         
         try {
            System.out.println("Removed: " + queue.take());
            System.out.println("Removed: " + queue.take());
            System.out.println("Removed: " + queue.take());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

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

เอาต์พุต

Added: 52
Removed: 52
Added: 70
Removed: 70
Added: 27
Removed: 27

อินเทอร์เฟซ java.util.concurrent.ConcurrentMap เป็นอินเทอร์เฟซย่อยของอินเทอร์เฟซแผนที่รองรับการทำงานของอะตอมบนตัวแปรแผนที่ที่อยู่เบื้องหลัง มันมีวิธีการรับและตั้งค่าที่ทำงานเหมือนอ่านและเขียนบนตัวแปรระเหย นั่นคือชุดมีความสัมพันธ์ที่เกิดขึ้นก่อนที่จะได้รับตัวแปรเดียวกันในภายหลัง อินเทอร์เฟซนี้รับประกันความปลอดภัยของเธรดและการรับประกันอะตอมมิก

วิธีการแผนที่พร้อมกัน

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

default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

พยายามคำนวณการแมปสำหรับคีย์ที่ระบุและค่าที่แมปปัจจุบัน (หรือค่าว่างหากไม่มีการแมปปัจจุบัน)

2

default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)

หากคีย์ที่ระบุไม่ได้เชื่อมโยงกับค่า (หรือถูกแมปกับ null) ให้พยายามคำนวณค่าโดยใช้ฟังก์ชันการแม็ปที่กำหนดและป้อนลงในแผนที่นี้เว้นแต่เป็นค่าว่าง

3

default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

หากค่าสำหรับคีย์ที่ระบุเป็นปัจจุบันและไม่ใช่ค่าว่างให้พยายามคำนวณการแมปใหม่ที่ให้คีย์และค่าที่แม็ปปัจจุบัน

4

default void forEach(BiConsumer<? super K,? super V> action)

ดำเนินการตามที่กำหนดสำหรับแต่ละรายการในแผนที่นี้จนกว่ารายการทั้งหมดจะได้รับการประมวลผลหรือการดำเนินการทำให้เกิดข้อยกเว้น

5

default V getOrDefault(Object key, V defaultValue)

ส่งคืนค่าที่คีย์ที่ระบุถูกแมปหรือ defaultValue หากแมปนี้ไม่มีการแมปสำหรับคีย์

6

default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

หากคีย์ที่ระบุไม่ได้เชื่อมโยงกับค่าหรือเชื่อมโยงกับ null ให้เชื่อมโยงกับค่าที่ไม่ใช่ค่าว่างที่กำหนด

7

V putIfAbsent(K key, V value)

หากคีย์ที่ระบุไม่ได้เชื่อมโยงกับค่าให้เชื่อมโยงกับค่าที่กำหนด

8

boolean remove(Object key, Object value)

ลบรายการสำหรับคีย์เฉพาะในขณะที่แมปกับค่าที่กำหนด

9

V replace(K key, V value)

แทนที่รายการสำหรับคีย์เฉพาะในขณะที่แมปกับค่าบางค่า

10

boolean replace(K key, V oldValue, V newValue)

แทนที่รายการสำหรับคีย์เฉพาะในขณะที่แมปกับค่าที่กำหนด

11

default void replaceAll(BiFunction<? super K,? super V,? extends V> function)

แทนที่ค่าของแต่ละรายการด้วยผลลัพธ์ของการเรียกใช้ฟังก์ชันที่กำหนดในรายการนั้นจนกว่ารายการทั้งหมดจะได้รับการประมวลผลหรือฟังก์ชันจะแสดงข้อยกเว้น

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ ConcurrentMap เทียบกับ HashMap

import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TestThread {

   public static void main(final String[] arguments) {
      Map<String,String> map = new ConcurrentHashMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial ConcurrentHashMap: " + map);
      Iterator<String> iterator = map.keySet().iterator();

      try { 
         
         while(iterator.hasNext()) {
            String key = iterator.next();
            
            if(key.equals("3")) {
               map.put("4", "Four");
            }
         }
      } catch(ConcurrentModificationException cme) {
         cme.printStackTrace();
      }
      System.out.println("ConcurrentHashMap after modification: " + map);

      map = new HashMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial HashMap: " + map);
      iterator = map.keySet().iterator();

      try {
         
         while(iterator.hasNext()) {
            String key = iterator.next();
            
            if(key.equals("3")) {
               map.put("4", "Four");
            }
         }
         System.out.println("HashMap after modification: " + map);
      } catch(ConcurrentModificationException cme) {
         cme.printStackTrace();
      }
   }  
}

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

เอาต์พุต

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
ConcurrentHashMap after modification: {1 = One, 2 = Two, 3 = Three, 4 = Four, 5 = Five, 6 = Six}
Initial HashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(Unknown Source)
	at java.util.HashMap$KeyIterator.next(Unknown Source)
	at TestThread.main(TestThread.java:48)

อินเทอร์เฟซ java.util.concurrent.ConcurrentNavigableMap เป็นอินเทอร์เฟซย่อยของอินเทอร์เฟซ ConcurrentMap และสนับสนุนการดำเนินการ NavigableMap และเรียกซ้ำสำหรับแผนที่ย่อยที่สามารถนำทางได้และการจับคู่โดยประมาณ

วิธีการแผนที่พร้อมกัน

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

NavigableSet<K> descendingKeySet()

ส่งคืนลำดับย้อนกลับมุมมอง NavigableSet ของคีย์ที่มีอยู่ในแผนที่นี้

2

ConcurrentNavigableMap<K,V> descendingMap()

ส่งคืนมุมมองลำดับย้อนกลับของการแมปที่มีอยู่ในแผนที่นี้

3

ConcurrentNavigableMap<K,V> headMap(K toKey)

ส่งคืนมุมมองของส่วนของแผนที่นี้ซึ่งมีคีย์น้อยกว่า toKey อย่างเคร่งครัด

4

ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive)

ส่งกลับมุมมองของส่วนของแผนที่นี้ที่มีคีย์น้อยกว่า (หรือเท่ากับถ้ารวมเป็นจริง) ให้กับคีย์

5

NavigableSet<K> keySet()

ส่งคืนมุมมอง NavigableSet ของคีย์ที่มีอยู่ในแผนที่นี้

6

NavigableSet<K> navigableKeySet()

ส่งคืนมุมมอง NavigableSet ของคีย์ที่มีอยู่ในแผนที่นี้

7

ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)

ส่งกลับมุมมองของส่วนต่างๆของแผนที่นี้ซึ่งมีคีย์ตั้งแต่คีย์ถึงคีย์

8

ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey)

ส่งคืนมุมมองของส่วนต่างๆของแผนที่นี้ซึ่งมีคีย์ตั้งแต่คีย์รวมถึงถึงคีย์พิเศษ

9

ConcurrentNavigableMap<K,V> tailMap(K fromKey)

ส่งคืนมุมมองของส่วนของแผนที่นี้ซึ่งมีคีย์มากกว่าหรือเท่ากับ fromKey

10

ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive)

ส่งคืนมุมมองของส่วนของแผนที่นี้ซึ่งมีคีย์มากกว่า (หรือเท่ากับถ้ารวมเป็นจริง) จากคีย์

ตัวอย่าง

โปรแกรม TestThread ต่อไปนี้แสดงการใช้ ConcurrentNavigableMap

import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

public class TestThread {

   public static void main(final String[] arguments) {
      ConcurrentNavigableMap<String,String> map =
         new ConcurrentSkipListMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial ConcurrentHashMap: "+map);
      System.out.println("HeadMap(\"2\") of ConcurrentHashMap: "+map.headMap("2"));
      System.out.println("TailMap(\"2\") of ConcurrentHashMap: "+map.tailMap("2"));
      System.out.println(
         "SubMap(\"2\", \"4\") of ConcurrentHashMap: "+map.subMap("2","4"));
   }  
}

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

เอาต์พุต

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
HeadMap("2") of ConcurrentHashMap: {1 = One}
TailMap("2") of ConcurrentHashMap: {2 = Two, 3 = Three, 5 = Five, 6 = Six}
SubMap("2", "4") of ConcurrentHashMap: {2 = Two, 3 = Three}