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
ส่งคืนคิวงานที่ใช้โดยตัวดำเนินการนี้ |
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}