Java Concurrency - Hướng dẫn nhanh
Java là một ngôn ngữ lập trình đa luồng có nghĩa là chúng ta có thể phát triển chương trình đa luồng bằng Java. Một chương trình đa luồng chứa hai hoặc nhiều phần có thể chạy đồng thời và mỗi phần có thể xử lý một tác vụ khác nhau cùng lúc để sử dụng tối ưu các tài nguyên có sẵn, đặc biệt khi máy tính của bạn có nhiều CPU.
Theo định nghĩa, đa nhiệm là khi nhiều quy trình chia sẻ tài nguyên xử lý chung như CPU. Đa luồng mở rộng ý tưởng về đa nhiệm vào các ứng dụng nơi bạn có thể chia nhỏ các hoạt động cụ thể trong một ứng dụng thành các luồng riêng lẻ. Mỗi luồng có thể chạy song song. Hệ điều hành phân chia thời gian xử lý không chỉ giữa các ứng dụng khác nhau mà còn giữa từng luồng trong một ứng dụng.
Đa luồng cho phép bạn viết theo cách mà nhiều hoạt động có thể tiến hành đồng thời trong cùng một chương trình.
Vòng đời của một sợi chỉ
Một luồng trải qua nhiều giai đoạn khác nhau trong vòng đời của nó. Ví dụ, một luồng được sinh ra, bắt đầu, chạy và sau đó chết. Sơ đồ sau đây cho thấy toàn bộ vòng đời của một luồng.
Sau đây là các giai đoạn của vòng đời:
New- Một luồng mới bắt đầu vòng đời của nó ở trạng thái mới. Nó vẫn ở trạng thái này cho đến khi chương trình bắt đầu luồng. Nó cũng được gọi làborn thread.
Runnable- Sau khi một luồng mới sinh được bắt đầu, luồng đó có thể chạy được. Một luồng ở trạng thái này được coi là đang thực thi nhiệm vụ của nó.
Waiting- Đôi khi, một luồng chuyển sang trạng thái chờ trong khi luồng đó chờ một luồng khác thực hiện một tác vụ. Một luồng chỉ chuyển trở lại trạng thái chạy được khi một luồng khác báo hiệu luồng đang chờ tiếp tục thực thi.
Timed Waiting- Một luồng có thể chạy được có thể vào trạng thái chờ được hẹn giờ trong một khoảng thời gian nhất định. Một luồng ở trạng thái này chuyển trở lại trạng thái có thể chạy khi khoảng thời gian đó hết hạn hoặc khi sự kiện mà nó đang chờ xảy ra.
Terminated (Dead) - Một luồng có thể chạy đi vào trạng thái kết thúc khi nó hoàn thành nhiệm vụ của nó hoặc kết thúc bằng cách khác.
Ưu tiên chuỗi
Mỗi luồng Java đều có một mức độ ưu tiên giúp hệ điều hành xác định thứ tự mà các luồng được lập lịch.
Mức độ ưu tiên của luồng Java nằm trong phạm vi giữa MIN_PRIORITY (hằng số 1) và MAX_PRIORITY (hằng số 10). Theo mặc định, mọi luồng được ưu tiên NORM_PRIORITY (hằng số 5).
Các luồng có mức độ ưu tiên cao hơn quan trọng hơn đối với một chương trình và nên được phân bổ thời gian xử lý trước các luồng có mức độ ưu tiên thấp hơn. Tuy nhiên, các ưu tiên của luồng không thể đảm bảo thứ tự mà các luồng thực thi và phụ thuộc rất nhiều vào nền tảng.
Tạo một chuỗi bằng cách triển khai một giao diện có thể chạy được
Nếu lớp của bạn dự định được thực thi dưới dạng một luồng thì bạn có thể đạt được điều này bằng cách triển khai Runnablegiao diện. Bạn sẽ cần làm theo ba bước cơ bản -
Bước 1
Bước đầu tiên, bạn cần triển khai phương thức run () được cung cấp bởi Runnablegiao diện. Phương thức này cung cấp một điểm vào cho luồng và bạn sẽ đưa logic nghiệp vụ hoàn chỉnh của mình vào bên trong phương pháp này. Sau đây là một cú pháp đơn giản của phương thức run ():
public void run( )
Bước 2
Bước thứ hai, bạn sẽ khởi tạo Thread đối tượng sử dụng hàm tạo sau:
Thread(Runnable threadObj, String threadName);
Trong đó, threadObj là một thể hiện của một lớp thực hiệnRunnable giao diện và threadName là tên được đặt cho chủ đề mới.
Bước 3
Sau khi một đối tượng Thread được tạo, bạn có thể bắt đầu nó bằng cách gọi start()phương thức thực thi một cuộc gọi đến phương thức run (). Sau đây là một cú pháp đơn giản của phương thức start ():
void start();
Example
Đây là một ví dụ tạo một chuỗi mới và bắt đầu chạy nó -
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();
}
}
Điều này sẽ tạo ra kết quả sau:
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.
Tạo một chủ đề bằng cách mở rộng một lớp chủ đề
Cách thứ hai để tạo một luồng là tạo một lớp mới mở rộng Threadlớp bằng hai bước đơn giản sau. Cách tiếp cận này cung cấp sự linh hoạt hơn trong việc xử lý nhiều luồng được tạo bằng các phương thức có sẵn trong lớp Thread.
Bước 1
Bạn sẽ cần ghi đè run( )phương thức có sẵn trong lớp Thread. Phương thức này cung cấp một điểm vào cho luồng và bạn sẽ đưa logic nghiệp vụ hoàn chỉnh của mình vào bên trong phương pháp này. Sau đây là một cú pháp đơn giản của phương thức run ():
public void run( )
Bước 2
Sau khi đối tượng Thread được tạo, bạn có thể bắt đầu nó bằng cách gọi start()phương thức thực thi một cuộc gọi đến phương thức run (). Sau đây là một cú pháp đơn giản của phương thức start ():
void start( );
Example
Đây là chương trình trước đó được viết lại để mở rộng Chủ đề -
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();
}
}
Điều này sẽ tạo ra kết quả sau:
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.
Trong chương này, chúng ta sẽ thảo luận về các khía cạnh khác nhau của việc thiết lập một môi trường tương thích cho Java.
Thiết lập môi trường cục bộ
Nếu bạn vẫn sẵn sàng thiết lập môi trường của mình cho ngôn ngữ lập trình Java, thì phần này sẽ hướng dẫn bạn cách tải xuống và thiết lập Java trên máy của bạn. Sau đây là các bước để thiết lập môi trường.
Java SE có sẵn miễn phí từ liên kết Tải xuống Java . Bạn có thể tải xuống phiên bản dựa trên hệ điều hành của mình.
Làm theo hướng dẫn để tải xuống Java và chạy .exeđể cài đặt Java trên máy của bạn. Khi bạn đã cài đặt Java trên máy của mình, bạn sẽ cần đặt các biến môi trường để trỏ đến các thư mục cài đặt chính xác -
Thiết lập đường dẫn cho Windows
Giả sử bạn đã cài đặt Java trong thư mục c: \ Program Files \ java \ jdk -
Nhấp chuột phải vào 'Máy tính của tôi' và chọn 'Thuộc tính'.
Nhấp vào nút 'Biến môi trường' trong tab 'Nâng cao'.
Bây giờ, hãy thay đổi biến 'Đường dẫn' để nó cũng chứa đường dẫn đến tệp thực thi Java. Ví dụ, nếu đường dẫn hiện được đặt thành 'C: \ WINDOWS \ SYSTEM32', thì hãy thay đổi đường dẫn của bạn thành 'C: \ WINDOWS \ SYSTEM32; c: \ Program Files \ java \ jdk \ bin'.
Thiết lập đường dẫn cho Linux, UNIX, Solaris, FreeBSD
Biến môi trường PATH nên được đặt để trỏ đến nơi các tệp nhị phân Java đã được cài đặt. Tham khảo tài liệu shell của bạn, nếu bạn gặp khó khăn khi thực hiện việc này.
Ví dụ, nếu bạn sử dụng bash làm trình bao, thì bạn sẽ thêm dòng sau vào cuối '.bashrc: export PATH = / path / to / java: $ PATH'
Trình chỉnh sửa Java phổ biến
Để viết các chương trình Java của bạn, bạn sẽ cần một trình soạn thảo văn bản. Thậm chí có nhiều IDE phức tạp hơn có sẵn trên thị trường. Nhưng hiện tại, bạn có thể xem xét một trong những điều sau:
Notepad - Trên máy Windows, bạn có thể sử dụng bất kỳ trình soạn thảo văn bản đơn giản nào như Notepad (Khuyến nghị cho hướng dẫn này), TextPad.
Netbeans - Một IDE Java là mã nguồn mở và miễn phí có thể được tải xuống từ https://netbeans.org/index.html.
Eclipse - Một IDE Java được phát triển bởi cộng đồng nguồn mở eclipse và có thể được tải xuống từ https://www.eclipse.org/.
Core Java cung cấp khả năng kiểm soát hoàn toàn chương trình đa luồng. Bạn có thể phát triển một chương trình đa luồng có thể bị tạm dừng, tiếp tục hoặc dừng hoàn toàn dựa trên yêu cầu của bạn. Có nhiều phương thức tĩnh khác nhau mà bạn có thể sử dụng trên các đối tượng luồng để kiểm soát hành vi của chúng. Bảng sau liệt kê các phương pháp đó:
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public void suspend() Phương thức này đặt một luồng ở trạng thái bị treo và có thể được tiếp tục lại bằng cách sử dụng phương thức resume (). |
2 | public void stop() Phương pháp này dừng một chủ đề hoàn toàn. |
3 | public void resume() Phương thức này tiếp tục một luồng đã bị tạm ngưng bằng cách sử dụng phương thức Susan (). |
4 | public void wait() Làm cho luồng hiện tại đợi cho đến khi một luồng khác gọi thông báo (). |
5 | public void notify() Đánh thức một luồng duy nhất đang chờ trên màn hình của đối tượng này. |
Hãy lưu ý rằng các phiên bản Java mới nhất đã không còn sử dụng các phương thức Susan (), resume () và stop () và vì vậy bạn cần sử dụng các phương thức thay thế có sẵn.
Thí dụ
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.");
}
}
Chương trình trên tạo ra kết quả sau:
Đầu ra
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.
Nếu bạn biết về giao tiếp giữa các quá trình thì bạn sẽ dễ dàng hiểu được giao tiếp giữa các luồng. Giao tiếp giữa các luồng rất quan trọng khi bạn phát triển một ứng dụng trong đó hai hoặc nhiều luồng trao đổi một số thông tin.
Có ba phương pháp đơn giản và một mẹo nhỏ giúp giao tiếp luồng có thể thực hiện được. Tất cả ba phương pháp được liệt kê dưới đây:
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public void wait() Làm cho luồng hiện tại đợi cho đến khi một luồng khác gọi thông báo (). |
2 | public void notify() Đánh thức một luồng duy nhất đang chờ trên màn hình của đối tượng này. |
3 | public void notifyAll() Đánh thức tất cả các luồng được gọi là wait () trên cùng một đối tượng. |
Các phương pháp này đã được thực hiện như finalcác phương thức trong Object, vì vậy chúng có sẵn trong tất cả các lớp. Cả ba phương thức chỉ có thể được gọi từ bên trongsynchronized bối cảnh.
Thí dụ
Ví dụ này cho thấy cách hai luồng có thể giao tiếp bằng cách sử dụng wait() và notify()phương pháp. Bạn có thể tạo một hệ thống phức tạp bằng cách sử dụng cùng một khái niệm.
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);
}
}
Khi chương trình trên được tuân thủ và thực thi, nó tạo ra kết quả sau:
Đầu ra
Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!
Ví dụ trên đã được thực hiện và sau đó được sửa đổi từ [https://stackoverflow.com/questions/2170520/inter-thread-communication-in-java]
Ví dụ về đa luồng với đồng bộ hóa
Đây là cùng một ví dụ in giá trị bộ đếm theo trình tự và mỗi khi chúng tôi chạy nó, nó sẽ cho ra kết quả giống nhau.
Thí dụ
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");
}
}
}
Điều này tạo ra cùng một kết quả mỗi khi bạn chạy chương trình này -
Đầu ra
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 mô tả một tình huống mà hai hoặc nhiều luồng bị chặn mãi mãi, chờ đợi nhau. Chốt lại xảy ra khi nhiều luồng cần các khóa giống nhau nhưng lấy chúng theo thứ tự khác nhau. Một chương trình đa luồng Java có thể gặp phải tình trạng deadlock vìsynchronizedtừ khóa khiến luồng thực thi bị chặn trong khi chờ khóa hoặc theo dõi, được liên kết với đối tượng được chỉ định. Đây là một ví dụ.
Thí dụ
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...");
}
}
}
}
}
Khi bạn biên dịch và thực thi chương trình trên, bạn thấy tình huống bế tắc và sau đây là kết quả do chương trình tạo ra:
Đầu ra
Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...
Chương trình trên sẽ bị treo vĩnh viễn vì không có luồng nào ở vị trí để tiến hành và chờ nhau giải phóng khóa, vì vậy bạn có thể thoát khỏi chương trình bằng cách nhấn CTRL + C.
Ví dụ về giải pháp bế tắc
Hãy thay đổi thứ tự của khóa và chạy cùng một chương trình để xem liệu cả hai chủ đề có còn chờ nhau không -
Thí dụ
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...");
}
}
}
}
}
Vì vậy, chỉ cần thay đổi thứ tự của các khóa ngăn chương trình rơi vào tình trạng bế tắc và hoàn thành với kết quả sau:
Đầu ra
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...
Ví dụ trên chỉ là để làm rõ khái niệm, tuy nhiên, đây là một khái niệm phức tạp và bạn nên đi sâu vào nó trước khi phát triển các ứng dụng của mình để đối phó với các tình huống bế tắc.
Lớp ThreadLocal được sử dụng để tạo các biến cục bộ của luồng chỉ có thể được đọc và ghi bởi cùng một luồng. Ví dụ: nếu hai luồng đang truy cập mã có tham chiếu đến cùng một biến threadLocal thì mỗi luồng sẽ không thấy bất kỳ sửa đổi nào đối với biến threadLocal được thực hiện bởi luồng khác.
Phương thức ThreadLocal
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp ThreadLocal.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public T get() Trả về giá trị trong bản sao luồng hiện tại của biến cục bộ luồng này. |
2 | protected T initialValue() Trả về "giá trị ban đầu" của luồng hiện tại cho biến cục bộ luồng này. |
3 | public void remove() Loại bỏ giá trị của luồng hiện tại cho biến cục bộ của luồng này. |
4 | public void set(T value) Đặt bản sao luồng hiện tại của biến cục bộ luồng này thành giá trị được chỉ định. |
Thí dụ
Chương trình TestThread sau đây trình bày một số phương thức này của lớp ThreadLocal. Ở đây chúng tôi đã sử dụng hai biến đếm, một là biến bình thường và một biến khác là 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");
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
Counter: 1
threadLocalCounter: 0
Counter: 2
threadLocalCounter: 0
Counter: 3
threadLocalCounter: 0
Counter: 4
threadLocalCounter: 0
Bạn có thể thấy giá trị của bộ đếm được tăng lên theo từng luồng, nhưng threadLocalCounter vẫn là 0 cho mỗi luồng.
Java.util.concurrent.ThreadLocalRandom là một lớp tiện ích được giới thiệu từ jdk 1.7 trở đi và rất hữu ích khi yêu cầu nhiều luồng hoặc ForkJoinTasks để tạo số ngẫu nhiên. Nó cải thiện hiệu suất và ít gây tranh cãi hơn phương thức Math.random ().
Phương thức ThreadLocalRandom
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp ThreadLocalRandom.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public static ThreadLocalRandom current() Trả về ThreadLocalRandom của luồng hiện tại. |
2 | protected int next(int bits) Tạo số giả ngẫu nhiên tiếp theo. |
3 | public double nextDouble(double n) Trả về giá trị kép giả, được phân phối đồng nhất giữa 0 (bao gồm) và giá trị được chỉ định (loại trừ). |
4 | public double nextDouble(double least, double bound) Trả về giá trị giả ngẫu nhiên, được phân phối đồng đều giữa giá trị nhỏ nhất đã cho (bao gồm) và bị ràng buộc (loại trừ). |
5 | public int nextInt(int least, int bound) Trả về giá trị giả ngẫu nhiên, được phân phối đồng đều giữa giá trị nhỏ nhất đã cho (bao gồm) và bị ràng buộc (loại trừ). |
6 | public long nextLong(long n) Trả về giá trị giả ngẫu nhiên, được phân phối đồng nhất giữa 0 (bao gồm) và giá trị được chỉ định (loại trừ). |
7 | public long nextLong(long least, long bound) Trả về giá trị giả ngẫu nhiên, được phân phối đồng đều giữa giá trị nhỏ nhất đã cho (bao gồm) và bị ràng buộc (loại trừ). |
số 8 | public void setSeed(long seed) Ném UnsupportedOperationException. |
Thí dụ
Chương trình TestThread sau đây trình bày một số phương pháp này của giao diện Khóa. Ở đây chúng tôi đã sử dụng lock () để có được khóa và unlock () để mở khóa.
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());
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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)
Ở đây chúng tôi đã sử dụng các lớp ThreadLocalRandom và Random để lấy các số ngẫu nhiên.
Giao diện java.util.concurrent.locks.Lock được sử dụng như một cơ chế đồng bộ hóa luồng tương tự như các khối được đồng bộ hóa. Cơ chế Khóa mới linh hoạt hơn và cung cấp nhiều tùy chọn hơn so với một khối được đồng bộ hóa. Sự khác biệt chính giữa Khóa và khối được đồng bộ hóa như sau:
Guarantee of sequence- Khối được đồng bộ hóa không cung cấp bất kỳ đảm bảo nào về trình tự trong đó luồng chờ sẽ được cấp quyền truy cập. Giao diện khóa xử lý nó.
No timeout- Khối được đồng bộ hóa không có tùy chọn thời gian chờ nếu khóa không được cấp. Giao diện khóa cung cấp tùy chọn như vậy.
Single method - Khối được đồng bộ hóa phải được chứa đầy đủ trong một phương thức trong khi các phương thức lock () và unlock () của giao diện khóa có thể được gọi trong các phương thức khác nhau.
Phương thức khóa
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp Khóa.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public void lock() Lấy khóa. |
2 | public void lockInterruptibly() Có được khóa trừ khi luồng hiện tại bị gián đoạn. |
3 | public Condition newCondition() Trả về một thể hiện Điều kiện mới được liên kết với thể hiện Khóa này. |
4 | public boolean tryLock() Chỉ nhận được khóa nếu nó còn trống tại thời điểm yêu cầu. |
5 | public boolean tryLock() Chỉ nhận được khóa nếu nó còn trống tại thời điểm yêu cầu. |
6 | public boolean tryLock(long time, TimeUnit unit) Có được khóa nếu nó rảnh trong thời gian chờ nhất định và luồng hiện tại không bị gián đoạn. |
7 | public void unlock() Mở khóa. |
Thí dụ
Chương trình TestThread sau đây trình bày một số phương pháp này của giao diện Khóa. Ở đây chúng tôi đã sử dụng lock () để có được khóa và unlock () để mở khóa.
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();
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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.
Chúng tôi đã sử dụng lớp ReentrantLock để triển khai giao diện Khóa ở đây. Lớp ReentrantLock cho phép một luồng khóa một phương thức ngay cả khi nó đã có khóa cho phương thức khác.
Giao diện java.util.concurrent.locks.ReadWriteLock cho phép nhiều luồng đọc cùng một lúc nhưng chỉ một luồng có thể ghi tại một thời điểm.
Read Lock - Nếu không có luồng nào khóa ReadWriteLock để ghi thì nhiều luồng có thể truy cập vào khóa đọc.
Write Lock - Nếu không có luồng nào đang đọc hoặc ghi, thì một luồng có thể truy cập vào khóa ghi.
Phương thức khóa
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp Khóa.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public Lock readLock() Trả về khóa được sử dụng để đọc. |
2 | public Lock writeLock() Trả về khóa được sử dụng để viết. |
Thí dụ
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
Writer A Time Taken 6 seconds.
Write Lock Present.
Writer B Time Taken 2 seconds.
Reader Time Taken 0 seconds.
Reader: aab
Giao diện java.util.concurrent.locks.Condition cung cấp khả năng tạm dừng thực thi của luồng cho đến khi điều kiện đã cho là đúng. Đối tượng Điều kiện nhất thiết phải được liên kết với Khóa và được lấy bằng phương thức newCondition ().
Phương pháp điều kiện
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp Điều kiện.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public void await() Làm cho luồng hiện tại đợi cho đến khi nó được báo hiệu hoặc bị ngắt. |
2 | public boolean await(long time, TimeUnit unit) Làm cho luồng hiện tại đợi cho đến khi nó được báo hiệu hoặc bị ngắt, hoặc thời gian chờ đã chỉ định trôi qua. |
3 | public long awaitNanos(long nanosTimeout) Làm cho luồng hiện tại đợi cho đến khi nó được báo hiệu hoặc bị ngắt, hoặc thời gian chờ đã chỉ định trôi qua. |
4 | public long awaitUninterruptibly() Làm cho luồng hiện tại đợi cho đến khi nó được báo hiệu. |
5 | public long awaitUntil() Làm cho luồng hiện tại đợi cho đến khi nó được báo hiệu hoặc bị gián đoạn, hoặc hết thời hạn quy định. |
6 | public void signal() Đánh thức một chuỗi đang chờ. |
7 | public void signalAll() Đánh thức tất cả các chủ đề đang chờ. |
Thí dụ
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
[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
Một lớp java.util.concurrent.atomic.AtomicInteger cung cấp các phép toán trên giá trị int cơ bản có thể đọc và viết nguyên tử, đồng thời chứa các phép toán nguyên tử nâng cao. AtomicInteger hỗ trợ các hoạt động nguyên tử trên biến int bên dưới. Nó có các phương thức get và set hoạt động như đọc và ghi trên các biến dễ bay hơi. Nghĩa là, một tập hợp có mối quan hệ xảy ra trước với bất kỳ lần nhận nào tiếp theo trên cùng một biến. Phương thức so sánh nguyên tử cũng có các tính năng nhất quán bộ nhớ này.
Phương thức AtomicInteger
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp AtomicInteger.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public int addAndGet(int delta) Nguyên tử thêm giá trị đã cho vào giá trị hiện tại. |
2 | public boolean compareAndSet(int expect, int update) Nguyên tử đặt giá trị thành giá trị cập nhật nhất định nếu giá trị hiện tại giống với giá trị mong đợi. |
3 | public int decrementAndGet() Nguyên tử giảm một giá trị hiện tại. |
4 | public double doubleValue() Trả về giá trị của một số được chỉ định dưới dạng nhân đôi. |
5 | public float floatValue() Trả về giá trị của số đã chỉ định dưới dạng số thực. |
6 | public int get() Nhận giá trị hiện tại. |
7 | public int getAndAdd(int delta) Atomiclly thêm giá trị đã cho vào giá trị hiện tại. |
số 8 | public int getAndDecrement() Nguyên tử giảm một giá trị hiện tại. |
9 | public int getAndIncrement() Nguyên tử tăng một giá trị hiện tại. |
10 | public int getAndSet(int newValue) Đặt nguyên tử thành giá trị đã cho và trả về giá trị cũ. |
11 | public int incrementAndGet() Nguyên tử tăng một giá trị hiện tại. |
12 | public int intValue() Trả về giá trị của số được chỉ định dưới dạng int. |
13 | public void lazySet(int newValue) Cuối cùng đặt thành giá trị đã cho. |
14 | public long longValue() Trả về giá trị của số được chỉ định dưới dạng dài. |
15 | public void set(int newValue) Đặt thành giá trị đã cho. |
16 | public String toString() Trả về biểu diễn chuỗi của giá trị hiện tại. |
17 | public boolean weakCompareAndSet(int expect, int update) Nguyên tử đặt giá trị thành giá trị cập nhật nhất định nếu giá trị hiện tại giống với giá trị mong đợi. |
Thí dụ
Chương trình TestThread sau đây cho thấy việc triển khai bộ đếm không an toàn trong môi trường dựa trên luồng.
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());
}
}
Điều này có thể tạo ra kết quả sau tùy thuộc vào tốc độ của máy tính và sự đan xen luồng.
Đầu ra
Final number (should be 1000): 1000
Thí dụ
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());
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
Final number (should be 1000): 1000
Một lớp java.util.concurrent.atomic.AtomicLong cung cấp các phép toán dựa trên giá trị dài cơ bản có thể đọc và viết nguyên tử, đồng thời cũng chứa các phép toán nguyên tử nâng cao. AtomicLong hỗ trợ các hoạt động nguyên tử trên biến dài cơ bản. Nó có các phương thức get và set hoạt động như đọc và ghi trên các biến dễ bay hơi. Nghĩa là, một tập hợp có mối quan hệ xảy ra trước với bất kỳ lần nhận nào tiếp theo trên cùng một biến. Phương thức so sánh nguyên tử cũng có các tính năng nhất quán bộ nhớ này.
Phương pháp AtomicLong
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp AtomicLong.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public long addAndGet(long delta) Nguyên tử thêm giá trị đã cho vào giá trị hiện tại. |
2 | public boolean compareAndSet(long expect, long update) Nguyên tử đặt giá trị thành giá trị cập nhật nhất định nếu giá trị hiện tại giống với giá trị mong đợi. |
3 | public long decrementAndGet() Nguyên tử giảm một giá trị hiện tại. |
4 | public double doubleValue() Trả về giá trị của một số được chỉ định dưới dạng nhân đôi. |
5 | public float floatValue() Trả về giá trị của số đã chỉ định dưới dạng số thực. |
6 | public long get() Nhận giá trị hiện tại. |
7 | public long getAndAdd(long delta) Atomiclly thêm giá trị đã cho vào giá trị hiện tại. |
số 8 | public long getAndDecrement() Nguyên tử giảm một giá trị hiện tại. |
9 | public long getAndIncrement() Nguyên tử tăng một giá trị hiện tại. |
10 | public long getAndSet(long newValue) Đặt nguyên tử thành giá trị đã cho và trả về giá trị cũ. |
11 | public long incrementAndGet() Nguyên tử tăng một giá trị hiện tại. |
12 | public int intValue() Trả về giá trị của số được chỉ định dưới dạng int. |
13 | public void lazySet(long newValue) Cuối cùng đặt thành giá trị đã cho. |
14 | public long longValue() Trả về giá trị của số được chỉ định dưới dạng dài. |
15 | public void set(long newValue) Đặt thành giá trị đã cho. |
16 | public String toString() Trả về biểu diễn chuỗi của giá trị hiện tại. |
17 | public boolean weakCompareAndSet(long expect, long update) Nguyên tử đặt giá trị thành giá trị cập nhật nhất định nếu giá trị hiện tại giống với giá trị mong đợi. |
Thí dụ
Chương trình TestThread sau đây cho thấy một cách triển khai bộ đếm an toàn bằng cách sử dụng AtomicLong trong môi trường dựa trên luồng.
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());
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
Final number (should be 1000): 1000
Lớp java.util.concurrent.atomic.AtomicBoolean cung cấp các phép toán trên giá trị boolean cơ bản có thể được đọc và viết theo nguyên tử, đồng thời chứa các phép toán nguyên tử nâng cao. AtomicBoolean hỗ trợ các hoạt động nguyên tử trên biến boolean cơ bản. Nó có các phương thức get và set hoạt động như đọc và ghi trên các biến dễ bay hơi. Nghĩa là, một tập hợp có mối quan hệ xảy ra trước với bất kỳ lần nhận nào tiếp theo trên cùng một biến. Phương thức so sánh nguyên tử cũng có các tính năng nhất quán bộ nhớ này.
Phương pháp AtomicBoolean
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp AtomicBoolean.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public boolean compareAndSet(boolean expect, boolean update) Nguyên tử đặt giá trị thành giá trị cập nhật nhất định nếu giá trị hiện tại == giá trị mong đợi. |
2 | public boolean get() Trả về giá trị hiện tại. |
3 | public boolean getAndSet(boolean newValue) Đặt nguyên tử thành giá trị đã cho và trả về giá trị trước đó. |
4 | public void lazySet(boolean newValue) Cuối cùng đặt thành giá trị đã cho. |
5 | public void set(boolean newValue) Đặt vô điều kiện đến giá trị đã cho. |
6 | public String toString() Trả về biểu diễn chuỗi của giá trị hiện tại. |
7 | public boolean weakCompareAndSet(boolean expect, boolean update) Nguyên tử đặt giá trị thành giá trị cập nhật nhất định nếu giá trị hiện tại == giá trị mong đợi. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng biến AtomicBoolean trong môi trường dựa trên luồng.
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();
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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!
Một lớp java.util.concurrent.atomic.AtomicReference cung cấp các hoạt động trên tham chiếu đối tượng cơ bản có thể được đọc và viết theo nguyên tử, đồng thời cũng chứa các phép toán nguyên tử nâng cao. AtomicReference hỗ trợ các hoạt động nguyên tử trên biến tham chiếu đối tượng cơ bản. Nó có các phương thức get và set hoạt động như đọc và ghi trên các biến dễ bay hơi. Nghĩa là, một tập hợp có mối quan hệ xảy ra trước với bất kỳ lần nhận nào tiếp theo trên cùng một biến. Phương thức so sánh nguyên tử cũng có các tính năng nhất quán bộ nhớ này.
Phương pháp tham khảo nguyên tử
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp AtomicReference.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public boolean compareAndSet(V expect, V update) Nguyên tử đặt giá trị thành giá trị cập nhật nhất định nếu giá trị hiện tại == giá trị mong đợi. |
2 | public boolean get() Trả về giá trị hiện tại. |
3 | public boolean getAndSet(V newValue) Đặt nguyên tử thành giá trị đã cho và trả về giá trị trước đó. |
4 | public void lazySet(V newValue) Cuối cùng đặt thành giá trị đã cho. |
5 | public void set(V newValue) Đặt vô điều kiện đến giá trị đã cho. |
6 | public String toString() Trả về biểu diễn chuỗi của giá trị hiện tại. |
7 | public boolean weakCompareAndSet(V expect, V update) Nguyên tử đặt giá trị thành giá trị cập nhật nhất định nếu giá trị hiện tại == giá trị mong đợi. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng biến AtomicReference trong môi trường dựa trên luồng.
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());
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
Message is: hello
Atomic Reference of Message is: Thread 1
Một lớp java.util.concurrent.atomic.AtomicIntegerArray cung cấp các phép toán trên mảng int bên dưới có thể đọc và viết nguyên tử, đồng thời chứa các phép toán nguyên tử nâng cao. AtomicIntegerArray hỗ trợ các hoạt động nguyên tử trên biến mảng int bên dưới. Nó có các phương thức get và set hoạt động như đọc và ghi trên các biến dễ bay hơi. Nghĩa là, một tập hợp có mối quan hệ xảy ra trước với bất kỳ lần nhận nào tiếp theo trên cùng một biến. Phương thức so sánh nguyên tử cũng có các tính năng nhất quán bộ nhớ này.
Phương thức AtomicIntegerArray
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp AtomicIntegerArray.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public int addAndGet(int i, int delta) Nguyên tử thêm giá trị đã cho vào phần tử ở chỉ số i. |
2 | public boolean compareAndSet(int i, int expect, int update) Đặt nguyên tử phần tử ở vị trí i thành giá trị cập nhật đã cho nếu giá trị hiện tại == giá trị mong đợi. |
3 | public int decrementAndGet(int i) Nguyên tử giảm một phần tử ở chỉ số i. |
4 | public int get(int i) Nhận giá trị hiện tại ở vị trí i. |
5 | public int getAndAdd(int i, int delta) Nguyên tử thêm giá trị đã cho vào phần tử ở chỉ số i. |
6 | public int getAndDecrement(int i) Nguyên tử giảm một phần tử ở chỉ số i. |
7 | public int getAndIncrement(int i) Nguyên tử tăng một phần tử ở chỉ số i. |
số 8 | public int getAndSet(int i, int newValue) Đặt nguyên tử phần tử ở vị trí i thành giá trị đã cho và trả về giá trị cũ. |
9 | public int incrementAndGet(int i) Nguyên tử tăng một phần tử ở chỉ số i. |
10 | public void lazySet(int i, int newValue) Cuối cùng đặt phần tử ở vị trí i thành giá trị đã cho. |
11 | public int length() Trả về độ dài của mảng. |
12 | public void set(int i, int newValue) Đặt phần tử ở vị trí i thành giá trị đã cho. |
13 | public String toString() Trả về biểu diễn chuỗi của các giá trị hiện tại của mảng. |
14 | public boolean weakCompareAndSet(int i, int expect, int update) Đặt nguyên tử phần tử ở vị trí i thành giá trị cập nhật đã cho nếu giá trị hiện tại == giá trị mong đợi. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng biến AtomicIntegerArray trong môi trường dựa trên luồng.
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");
}
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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
Một lớp java.util.concurrent.atomic.AtomicLongArray cung cấp các phép toán trên mảng dài bên dưới có thể đọc và viết nguyên tử, đồng thời chứa các phép toán nguyên tử nâng cao. AtomicLongArray hỗ trợ các hoạt động nguyên tử trên biến mảng dài bên dưới. Nó có các phương thức get và set hoạt động như đọc và ghi trên các biến dễ bay hơi. Nghĩa là, một tập hợp có mối quan hệ xảy ra trước với bất kỳ lần nhận nào tiếp theo trên cùng một biến. Phương thức so sánh nguyên tử cũng có các tính năng nhất quán bộ nhớ này.
Phương thức AtomicLongArray
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp AtomicLongArray.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public long addAndGet(int i, long delta) Nguyên tử thêm giá trị đã cho vào phần tử ở chỉ số i. |
2 | public boolean compareAndSet(int i, long expect, long update) Đặt nguyên tử phần tử ở vị trí i thành giá trị cập nhật đã cho nếu giá trị hiện tại == giá trị mong đợi. |
3 | public long decrementAndGet(int i) Nguyên tử giảm một phần tử ở chỉ số i. |
4 | public long get(int i) Nhận giá trị hiện tại ở vị trí i. |
5 | public long getAndAdd(int i, long delta) Nguyên tử thêm giá trị đã cho vào phần tử ở chỉ số i. |
6 | public long getAndDecrement(int i) Nguyên tử giảm một phần tử ở chỉ số i. |
7 | public long getAndIncrement(int i) Nguyên tử tăng một phần tử ở chỉ số i. |
số 8 | public long getAndSet(int i, long newValue) Đặt nguyên tử phần tử ở vị trí i thành giá trị đã cho và trả về giá trị cũ. |
9 | public long incrementAndGet(int i) Nguyên tử tăng một phần tử ở chỉ số i. |
10 | public void lazySet(int i, long newValue) Cuối cùng đặt phần tử ở vị trí i thành giá trị đã cho. |
11 | public int length() Trả về độ dài của mảng. |
12 | public void set(int i, long newValue) Đặt phần tử ở vị trí i thành giá trị đã cho. |
13 | public String toString() Trả về biểu diễn chuỗi của các giá trị hiện tại của mảng. |
14 | public boolean weakCompareAndSet(int i, long expect, long update) Đặt nguyên tử phần tử ở vị trí i thành giá trị cập nhật đã cho nếu giá trị hiện tại == giá trị mong đợi. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng biến AtomicIntegerArray trong môi trường dựa trên luồng.
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");
}
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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
Lớp java.util.concurrent.atomic.AtomicReferenceArray cung cấp các hoạt động trên mảng tham chiếu bên dưới có thể được đọc và viết theo nguyên tử, đồng thời chứa các phép toán nguyên tử nâng cao. AtomicReferenceArray hỗ trợ các hoạt động nguyên tử trên biến mảng tham chiếu bên dưới. Nó có các phương thức get và set hoạt động như đọc và ghi trên các biến dễ bay hơi. Nghĩa là, một tập hợp có mối quan hệ xảy ra trước với bất kỳ lần nhận nào tiếp theo trên cùng một biến. Phương thức so sánh nguyên tử cũng có các tính năng nhất quán bộ nhớ này.
Phương thức AtomicReferenceArray
Sau đây là danh sách các phương thức quan trọng có sẵn trong lớp AtomicReferenceArray.
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | public boolean compareAndSet(int i, E expect, E update) Đặt nguyên tử phần tử ở vị trí i thành giá trị cập nhật đã cho nếu giá trị hiện tại == giá trị mong đợi. |
2 | public E get(int i) Nhận giá trị hiện tại ở vị trí i. |
3 | public E getAndSet(int i, E newValue) Đặt nguyên tử phần tử ở vị trí i thành giá trị đã cho và trả về giá trị cũ. |
4 | public void lazySet(int i, E newValue) Cuối cùng đặt phần tử ở vị trí i thành giá trị đã cho. |
5 | public int length() Trả về độ dài của mảng. |
6 | public void set(int i, E newValue) Đặt phần tử ở vị trí i thành giá trị đã cho. |
7 | public String toString() Trả về biểu diễn chuỗi của các giá trị hiện tại của mảng. |
số 8 | public boolean weakCompareAndSet(int i, E expect, E update) Đặt nguyên tử phần tử ở vị trí i thành giá trị cập nhật đã cho nếu giá trị hiện tại == giá trị mong đợi. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng biến AtomicReferenceArray trong môi trường dựa trên luồng.
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");
}
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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
Giao diện java.util.concurrent.Executor là một giao diện đơn giản để hỗ trợ khởi chạy các tác vụ mới.
Phương thức ExecutorService
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | void execute(Runnable command) Thực hiện lệnh đã cho vào một thời điểm nào đó trong tương lai. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng giao diện Executor trong môi trường dựa trên luồng.
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
Running Task!
Task Completed
Giao diện java.util.concurrent.ExecutorService là giao diện con của giao diện Executor và thêm các tính năng để quản lý vòng đời, cả các tác vụ riêng lẻ và của chính trình thực thi.
Phương thức ExecutorService
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | boolean awaitTermination(long timeout, TimeUnit unit) Chặn cho đến khi tất cả các tác vụ đã hoàn thành thực thi sau khi yêu cầu tắt máy hoặc hết thời gian chờ xảy ra hoặc luồng hiện tại bị gián đoạn, tùy điều kiện nào xảy ra trước. |
2 | <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) Thực thi các nhiệm vụ đã cho, trả về danh sách các Hợp đồng tương lai giữ trạng thái và kết quả của họ khi tất cả hoàn thành. |
3 | <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) Thực thi các nhiệm vụ đã cho, trả về danh sách các Hợp đồng tương lai giữ trạng thái và kết quả của chúng khi tất cả hoàn thành hoặc hết thời gian chờ, tùy điều kiện nào xảy ra trước. |
4 | <T> T invokeAny(Collection<? extends Callable<T>> tasks) Thực hiện các nhiệm vụ đã cho, trả về kết quả của một nhiệm vụ đã hoàn thành thành công (tức là không đưa ra ngoại lệ), nếu có. |
5 | <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) Thực thi các nhiệm vụ đã cho, trả về kết quả của một nhiệm vụ đã hoàn thành thành công (tức là không đưa ra ngoại lệ), nếu có trước khi hết thời gian chờ đã cho. |
6 | boolean isShutdown() Trả về true nếu trình thực thi này đã bị tắt. |
7 | boolean isTerminated() Trả về true nếu tất cả các tác vụ đã hoàn thành sau khi tắt. |
số 8 | void shutdown() Bắt đầu tắt theo thứ tự trong đó các nhiệm vụ đã gửi trước đó được thực thi, nhưng không có nhiệm vụ mới nào được chấp nhận. |
9 | List<Runnable> shutdownNow() Cố gắng dừng tất cả các tác vụ đang thực thi, tạm dừng xử lý các tác vụ đang chờ và trả về danh sách các tác vụ đang chờ thực thi. |
10 | <T> Future<T> submit(Callable<T> task) Gửi một nhiệm vụ trả về giá trị để thực thi và trả về Tương lai đại diện cho kết quả đang chờ xử lý của nhiệm vụ. |
11 | Future<?> submit(Runnable task) Gửi một tác vụ Runnable để thực thi và trả về Tương lai đại diện cho tác vụ đó. |
12 | <T> Future<T> submit(Runnable task, T result) Gửi một tác vụ Runnable để thực thi và trả về Tương lai đại diện cho tác vụ đó. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng giao diện ExecutorService trong môi trường dựa trên luồng.
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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)
Giao diện java.util.concurrent.SchedisedExecutorService là giao diện con của giao diện ExecutorService và hỗ trợ thực thi các tác vụ trong tương lai và / hoặc định kỳ.
Các phương thức SchedisedExecutorService
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) Tạo và thực thi một SchedisedFuture được bật sau độ trễ nhất định. |
2 | ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) Tạo và thực hiện hành động một lần được bật sau độ trễ nhất định. |
3 | ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) Tạo và thực hiện một hành động định kỳ được kích hoạt đầu tiên sau độ trễ ban đầu nhất định và sau đó với khoảng thời gian nhất định; nghĩa là các quá trình thực thi sẽ bắt đầu sau thời gian ban đầu (InitialDelay) rồi đến thời gian ban đầu + thời gian, sau đó là dấu chấm hết thời gian ban đầu + 2 *, v.v. |
4 | ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) Tạo và thực hiện một hành động định kỳ được kích hoạt đầu tiên sau độ trễ ban đầu nhất định và sau đó với độ trễ nhất định giữa việc kết thúc một lần thực hiện và bắt đầu lần tiếp theo. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng giao diện SchedisedExecutorService trong môi trường dựa trên luồng.
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");
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
beep
beep
beep
beep
Một nhóm luồng cố định có thể được tạo bằng cách gọi phương thức newFixedThreadPool () tĩnh của lớp Executor.
Cú pháp
ExecutorService fixedPool = Executors.newFixedThreadPool(2);
Ở đâu
Tối đa 2 luồng sẽ hoạt động để xử lý tác vụ.
Nếu nhiều hơn 2 chủ đề được gửi thì chúng được giữ trong một hàng đợi cho đến khi các chủ đề có sẵn.
Một luồng mới được tạo ra để thế chỗ nếu một luồng kết thúc do lỗi trong quá trình tắt thực thi trên trình thực thi vẫn chưa được gọi.
Mọi chủ đề tồn tại cho đến khi nhóm ngừng hoạt động.
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng phương thức newFixedThreadPool trong môi trường dựa trên luồng.
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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
Nhóm luồng được lưu trong bộ nhớ cache có thể được tạo ra bằng cách gọi phương thức newCachedThreadPool () tĩnh của lớp Executor.
Cú pháp
ExecutorService executor = Executors.newCachedThreadPool();
Ở đâu
Phương thức newCachedThreadPool tạo ra một trình thực thi có một nhóm luồng có thể mở rộng.
Trình thực thi như vậy phù hợp với các ứng dụng khởi chạy nhiều tác vụ trong thời gian ngắn.
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng phương thức newCachedThreadPool trong môi trường dựa trên luồng.
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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
Nhóm luồng đã lên lịch có thể được hoàn thành bằng cách gọi phương thức static newSchedonedThreadPool () của lớp Executor.
Cú pháp
ExecutorService executor = Executors.newScheduledThreadPool(1);
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng phương thức newSchedonedThreadPool trong môi trường dựa trên luồng.
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");
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
beep
beep
beep
beep
Một nhóm luồng đơn có thể được tạo ra bằng cách gọi phương thức static newSingleThreadExecutor () của lớp Executor.
Cú pháp
ExecutorService executor = Executors.newSingleThreadExecutor();
Trường hợp phương thức newSingleThreadExecutor tạo ra một trình thực thi thực thi một tác vụ duy nhất tại một thời điểm.
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng phương thức newSingleThreadExecutor trong môi trường dựa trên luồng.
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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. Nó cũng cung cấp các phương pháp tiện ích khác nhau để kiểm tra số liệu thống kê chủ đề hiện tại và kiểm soát chúng.
Phương thức ThreadPoolExecutor
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | protected void afterExecute(Runnable r, Throwable t) Phương thức được gọi sau khi hoàn thành việc thực thi Runnable đã cho. |
2 | void allowCoreThreadTimeOut(boolean value) Đặt chính sách điều chỉnh liệu các luồng lõi có thể hết thời gian chờ và kết thúc nếu không có tác vụ nào đến trong thời gian duy trì, được thay thế nếu cần khi tác vụ mới đến. |
3 | boolean allowsCoreThreadTimeOut() Trả về true nếu nhóm này cho phép các luồng lõi hết thời gian chờ và kết thúc nếu không có tác vụ nào đến trong thời gian keepAlive, được thay thế nếu cần khi có tác vụ mới. |
4 | boolean awaitTermination(long timeout, TimeUnit unit) Chặn cho đến khi tất cả các tác vụ đã hoàn thành thực thi sau khi yêu cầu tắt máy hoặc hết thời gian chờ xảy ra hoặc luồng hiện tại bị gián đoạn, tùy điều kiện nào xảy ra trước. |
5 | protected void beforeExecute(Thread t, Runnable r) Phương thức được gọi trước khi thực thi Runnable đã cho trong luồng đã cho. |
6 | void execute(Runnable command) Thực thi nhiệm vụ đã cho vào một lúc nào đó trong tương lai. |
7 | protected void finalize() Gọi tắt khi trình thực thi này không còn được tham chiếu và nó không có luồng. |
số 8 | int getActiveCount() Trả về số luồng gần đúng đang thực thi tác vụ. |
9 | long getCompletedTaskCount() Trả về tổng số tác vụ gần đúng đã hoàn thành việc thực thi. |
10 | int getCorePoolSize() Trả về số chủ đề cốt lõi. |
11 | long getKeepAliveTime(TimeUnit unit) Trả về thời gian tồn tại của luồng, là khoảng thời gian mà các luồng vượt quá kích thước nhóm lõi có thể không hoạt động trước khi kết thúc. |
12 | int getLargestPoolSize() Trả về số luồng lớn nhất đã từng có trong nhóm. |
13 | int getMaximumPoolSize() Trả về số luồng tối đa cho phép. |
14 | int getPoolSize() Trả về số luồng hiện tại trong nhóm. |
15 | BlockingQueue
Trả về hàng đợi tác vụ được sử dụng bởi trình thực thi này. |
15 | RejectedExecutionHandler getRejectedExecutionHandler() Trả về trình xử lý hiện tại cho các tác vụ không thể thực thi. |
16 | long getTaskCount() Trả về tổng số công việc gần đúng đã từng được lên lịch thực hiện. |
17 | ThreadFactory getThreadFactory() Trả về nhà máy luồng được sử dụng để tạo luồng mới. |
18 | boolean isShutdown() Trả về true nếu trình thực thi này đã bị tắt. |
19 | boolean isTerminated() Trả về true nếu tất cả các tác vụ đã hoàn thành sau khi tắt. |
20 | boolean isTerminating() Trả về true nếu trình thực thi này đang trong quá trình kết thúc sau khi shutdown () hoặc shutdownNow () nhưng chưa kết thúc hoàn toàn. |
21 | int prestartAllCoreThreads() Bắt đầu tất cả các luồng lõi, khiến chúng phải chờ đợi công việc. |
22 | boolean prestartCoreThread() Bắt đầu một chuỗi cốt lõi, khiến nó không hoạt động chờ đợi. |
23 | void purge() Cố gắng xóa khỏi hàng đợi công việc tất cả các nhiệm vụ trong Tương lai đã bị hủy bỏ. |
24 | boolean remove(Runnable task) Loại bỏ tác vụ này khỏi hàng đợi nội bộ của trình thực thi nếu nó hiện diện, do đó khiến nó không được chạy nếu nó chưa bắt đầu. |
25 | void setCorePoolSize(int corePoolSize) Đặt số lượng chủ đề cốt lõi. |
26 | void setKeepAliveTime(long time, TimeUnit unit) Đặt giới hạn thời gian mà các luồng có thể không hoạt động trước khi bị chấm dứt. |
27 | void setMaximumPoolSize(int maximumPoolSize) Đặt số lượng chủ đề tối đa cho phép. |
28 | void setRejectedExecutionHandler(RejectedExecutionHandler handler) Đặt một trình xử lý mới cho các tác vụ không thể thực thi. |
29 | void setThreadFactory(ThreadFactory threadFactory) Đặt nhà máy sản xuất luồng được sử dụng để tạo luồng mới. |
30 | void shutdown() Bắt đầu tắt theo thứ tự trong đó các nhiệm vụ đã gửi trước đó được thực thi, nhưng không có nhiệm vụ mới nào được chấp nhận. |
31 | List<Runnable> shutdownNow() Cố gắng dừng tất cả các tác vụ đang thực thi, tạm dừng xử lý các tác vụ đang chờ và trả về danh sách các tác vụ đang chờ thực thi. |
32 | protected void terminated() Phương thức được gọi khi Người thực thi đã kết thúc. |
33 | String toString() Trả về một chuỗi xác định nhóm này, cũng như trạng thái của nó, bao gồm các chỉ báo về trạng thái chạy và số lượng công nhân và tác vụ ước tính. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng giao diện ThreadPoolExecutor trong môi trường dựa trên luồng.
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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.SchedisedThreadPoolExecutor là một lớp con của ThreadPoolExecutor và có thể lên lịch bổ sung các lệnh để chạy sau một khoảng thời gian trễ nhất định hoặc thực thi định kỳ.
Các phương thức SchedisedThreadPoolExecutor
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) Sửa đổi hoặc thay thế tác vụ được sử dụng để thực thi một lệnh có thể gọi. |
2 | protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) Sửa đổi hoặc thay thế tác vụ được sử dụng để thực thi một lệnh có thể chạy được. |
3 | void execute(Runnable command) Thực hiện lệnh với độ trễ yêu cầu bằng 0. |
4 | boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() Nhận chính sách về việc có tiếp tục thực hiện các tác vụ định kỳ hiện có ngay cả khi trình thực thi này đã bị tắt hay không. |
5 | boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() Nhận chính sách về việc có thực thi các tác vụ bị trì hoãn hiện tại hay không ngay cả khi trình thực thi này đã bị tắt. |
6 | BlockingQueue<Runnable> getQueue() Trả về hàng đợi tác vụ được sử dụng bởi trình thực thi này. |
7 | boolean getRemoveOnCancelPolicy() Nhận chính sách về việc liệu các tác vụ đã hủy có nên được xóa ngay lập tức khỏi hàng đợi công việc tại thời điểm hủy hay không. |
số 8 | <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) Tạo và thực thi một SchedisedFuture được bật sau độ trễ nhất định. |
9 | ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) Tạo và thực hiện hành động một lần được bật sau độ trễ nhất định. |
10 | ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) Tạo và thực hiện một hành động định kỳ được kích hoạt đầu tiên sau độ trễ ban đầu nhất định và sau đó với khoảng thời gian nhất định; nghĩa là các quá trình thực thi sẽ bắt đầu sau thời gian ban đầu (InitialDelay) rồi đến thời gian ban đầu + thời gian, sau đó là dấu chấm hết thời gian ban đầu + 2 *, v.v. |
11 | ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) Tạo và thực hiện một hành động định kỳ được kích hoạt đầu tiên sau độ trễ ban đầu nhất định và sau đó với độ trễ nhất định giữa việc kết thúc một lần thực hiện và bắt đầu lần tiếp theo. |
12 | void setContinueExistingPeriodicTasksAfterShutdownPolicy (boolean value) Đặt chính sách về việc có tiếp tục thực thi các tác vụ định kỳ hiện có ngay cả khi trình thực thi này đã bị tắt hay không. |
13 | void setExecuteExistingDelayedTasksAfterShutdownPolicy (boolean value) Đặt chính sách về việc có thực thi các tác vụ bị trì hoãn hiện tại hay không ngay cả khi trình thực thi này đã bị tắt. |
14 | void setRemoveOnCancelPolicy(boolean value) Đặt chính sách về việc liệu các nhiệm vụ đã hủy có được xóa ngay lập tức khỏi hàng đợi công việc tại thời điểm hủy hay không. |
15 | void shutdown() Bắt đầu tắt theo thứ tự trong đó các nhiệm vụ đã gửi trước đó được thực thi, nhưng không có nhiệm vụ mới nào được chấp nhận. |
16 | List<Runnable> shutdownNow() Cố gắng dừng tất cả các tác vụ đang thực thi, tạm dừng xử lý các tác vụ đang chờ và trả về danh sách các tác vụ đang chờ thực thi. |
17 | <T> Future<T> submit(Callable<T> task) Gửi một nhiệm vụ trả về giá trị để thực thi và trả về Tương lai đại diện cho kết quả đang chờ xử lý của nhiệm vụ. |
18 | Future<?> submit(Runnable task) Gửi một tác vụ Runnable để thực thi và trả về Tương lai đại diện cho tác vụ đó. |
19 | <T> Future<T> submit(Runnable task, T result) Gửi một tác vụ Runnable để thực thi và trả về Tương lai đại diện cho tác vụ đó. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng giao diện SchedisedThreadPoolExecutor trong môi trường dựa trên luồng.
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");
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
beep
beep
beep
beep
Đối tượng java.util.concurrent.Callable có thể trả về kết quả được tính toán được thực hiện bởi một luồng, ngược lại với giao diện runnable chỉ có thể chạy luồng. Đối tượng Callable trả về đối tượng Future cung cấp các phương thức để theo dõi tiến trình của một tác vụ đang được thực thi bởi một luồng. Đối tượng trong tương lai có thể được sử dụng để kiểm tra trạng thái của một Callable và sau đó truy xuất kết quả từ Callable sau khi chuỗi hoàn thành. Nó cũng cung cấp chức năng thời gian chờ.
Cú pháp
//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();
Thí dụ
Chương trình TestThread sau đây cho thấy việc sử dụng Futures và Callable trong môi trường dựa trên luồng.
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;
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
Factorial Service called for 10!
Factorial Service called for 20!
10! = 3628800
20! = 2432902008176640000
Khuôn khổ tham gia fork cho phép phá vỡ một nhiệm vụ nhất định trên một số công nhân và sau đó chờ kết quả để kết hợp chúng. Nó tận dụng công suất của máy đa bộ xử lý ở mức độ lớn. Sau đây là các khái niệm và đối tượng cốt lõi được sử dụng trong khuôn khổ fork-join.
Cái nĩa
Fork là một quá trình trong đó một nhiệm vụ tự phân chia thành các nhiệm vụ con nhỏ hơn và độc lập có thể được thực hiện đồng thời.
Cú pháp
Sum left = new Sum(array, low, mid);
left.fork();
Ở đây Sum là một lớp con của RecursiveTask và left.fork () tách nhiệm vụ thành các nhiệm vụ con.
Tham gia
Tham gia là một quá trình trong đó một nhiệm vụ kết hợp tất cả các kết quả của các nhiệm vụ con sau khi các nhiệm vụ con đã thực hiện xong, nếu không nó sẽ tiếp tục chờ đợi.
Cú pháp
left.join();
Ở đây bên trái là một đối tượng của lớp Sum.
ForkJoinPool
nó là một nhóm chủ đề đặc biệt được thiết kế để hoạt động với phân tách nhiệm vụ fork-và-join.
Cú pháp
ForkJoinPool forkJoinPool = new ForkJoinPool(4);
Đây là ForkJoinPool mới với cấp độ song song của 4 CPU.
RecursiveAction
RecursiveAction đại diện cho một tác vụ không trả về bất kỳ giá trị nào.
Cú pháp
class Writer extends RecursiveAction {
@Override
protected void compute() { }
}
RecursiveTask
RecursiveTask đại diện cho một tác vụ trả về một giá trị.
Cú pháp
class Sum extends RecursiveTask<Long> {
@Override
protected Long compute() { return null; }
}
Thí dụ
Chương trình TestThread sau đây cho thấy việc sử dụng khung Fork-Join trong môi trường dựa trên luồng.
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;
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
32
499500
Giao diện java.util.concurrent.BlockingQueue là giao diện con của giao diện Hàng đợi và hỗ trợ thêm các hoạt động như chờ đợi hàng đợi trở nên trống trước khi truy xuất một phần tử và đợi khoảng trống có sẵn trong hàng đợi trước khi lưu trữ một phần tử .
Phương thức BlockingQueue
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | boolean add(E e) Chèn phần tử được chỉ định vào hàng đợi này nếu có thể làm như vậy ngay lập tức mà không vi phạm các giới hạn dung lượng, trả về true khi thành công và ném IllegalStateException nếu không có dung lượng. |
2 | boolean contains(Object o) Trả về true nếu hàng đợi này chứa phần tử được chỉ định. |
3 | int drainTo(Collection<? super E> c) Loại bỏ tất cả các phần tử có sẵn khỏi hàng đợi này và thêm chúng vào bộ sưu tập đã cho. |
4 | int drainTo(Collection<? super E> c, int maxElements) Loại bỏ tối đa số phần tử có sẵn đã cho khỏi hàng đợi này và thêm chúng vào bộ sưu tập đã cho. |
5 | boolean offer(E e) Chèn phần tử được chỉ định vào hàng đợi này nếu có thể làm như vậy ngay lập tức mà không vi phạm các giới hạn dung lượng, trả về true khi thành công và false nếu hiện không còn chỗ trống. |
6 | boolean offer(E e, long timeout, TimeUnit unit) Chèn phần tử được chỉ định vào hàng đợi này, đợi đến thời gian chờ được chỉ định nếu cần thiết để có chỗ trống. |
7 | E poll(long timeout, TimeUnit unit) Lấy và loại bỏ phần đầu của hàng đợi này, đợi đến thời gian chờ được chỉ định nếu cần thiết để một phần tử có sẵn. |
số 8 | void put(E e) Chèn phần tử được chỉ định vào hàng đợi này, đợi nếu cần thiết để có dung lượng. |
9 | int remainingCapacity() Trả về số phần tử bổ sung mà hàng đợi này lý tưởng có thể chấp nhận (trong trường hợp không có bộ nhớ hoặc ràng buộc tài nguyên) mà không bị chặn hoặc Integer.MAX_VALUE nếu không có giới hạn nội tại. |
10 | boolean remove(Object o) Loại bỏ một phiên bản của phần tử được chỉ định khỏi hàng đợi này, nếu nó có mặt. |
11 | E take() Lấy và loại bỏ phần đầu của hàng đợi này, đợi nếu cần cho đến khi một phần tử có sẵn. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng giao diện BlockingQueue trong môi trường dựa trên luồng.
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();
}
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
Added: 52
Removed: 52
Added: 70
Removed: 70
Added: 27
Removed: 27
Giao diện java.util.concurrent.ConcurrentMap là giao diện con của giao diện Bản đồ, hỗ trợ các hoạt động nguyên tử trên biến bản đồ bên dưới. Nó có các phương thức get và set hoạt động như đọc và ghi trên các biến dễ bay hơi. Nghĩa là, một tập hợp có mối quan hệ xảy ra trước với bất kỳ lần nhận nào tiếp theo trên cùng một biến. Giao diện này đảm bảo an toàn luồng và đảm bảo tính nguyên tử.
Phương thức ConcurrentMap
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) Cố gắng tính toán ánh xạ cho khóa được chỉ định và giá trị được ánh xạ hiện tại của nó (hoặc null nếu không có ánh xạ hiện tại). |
2 | default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) Nếu khóa được chỉ định chưa được liên kết với một giá trị (hoặc được ánh xạ thành null), hãy cố gắng tính toán giá trị của nó bằng cách sử dụng hàm ánh xạ đã cho và nhập nó vào bản đồ này trừ khi null. |
3 | default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) Nếu giá trị cho khóa được chỉ định có mặt và không rỗng, hãy cố gắng tính ánh xạ mới cho khóa và giá trị được ánh xạ hiện tại của nó. |
4 | default void forEach(BiConsumer<? super K,? super V> action) Thực hiện hành động đã cho cho từng mục nhập trong bản đồ này cho đến khi tất cả mục nhập đã được xử lý hoặc hành động ném ra một ngoại lệ. |
5 | default V getOrDefault(Object key, V defaultValue) Trả về giá trị mà khóa đã chỉ định được ánh xạ hoặc defaultValue nếu bản đồ này không chứa ánh xạ cho khóa. |
6 | default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) Nếu khóa được chỉ định chưa được liên kết với một giá trị hoặc được liên kết với null, hãy liên kết nó với giá trị không null đã cho. |
7 | V putIfAbsent(K key, V value) Nếu khóa được chỉ định chưa được liên kết với một giá trị, hãy liên kết nó với giá trị đã cho. |
số 8 | boolean remove(Object key, Object value) Chỉ xóa mục nhập cho một khóa nếu hiện được ánh xạ tới một giá trị nhất định. |
9 | V replace(K key, V value) Chỉ thay thế mục nhập cho một khóa nếu hiện được ánh xạ tới một giá trị nào đó. |
10 | boolean replace(K key, V oldValue, V newValue) Chỉ thay thế mục nhập cho một khóa nếu hiện được ánh xạ tới một giá trị nhất định. |
11 | default void replaceAll(BiFunction<? super K,? super V,? extends V> function) Thay thế giá trị của mỗi mục nhập bằng kết quả của việc gọi hàm đã cho trên mục nhập đó cho đến khi tất cả các mục nhập đã được xử lý hoặc hàm ném ra một ngoại lệ. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng ConcurrentMap và 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();
}
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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)
Giao diện java.util.concurrent.ConcurrentNavigableMap là giao diện con của giao diện ConcurrentMap và hỗ trợ các hoạt động của NavigableMap và đệ quy như vậy đối với các bản đồ con có thể điều hướng và các kết quả phù hợp gần đúng.
Phương thức ConcurrentMap
Sr.No. | Phương pháp & Mô tả |
---|---|
1 | NavigableSet<K> descendingKeySet() Trả về một thứ tự ngược lại dạng xem NavigableSet của các khóa có trong bản đồ này. |
2 | ConcurrentNavigableMap<K,V> descendingMap() Trả về chế độ xem thứ tự ngược lại của các ánh xạ có trong bản đồ này. |
3 | ConcurrentNavigableMap<K,V> headMap(K toKey) Trả về chế độ xem một phần của bản đồ này có các khóa hoàn toàn nhỏ hơn toKey. |
4 | ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive) Trả về chế độ xem một phần của bản đồ này có các khóa nhỏ hơn (hoặc bằng, nếu tính toàn bộ là đúng) toKey. |
5 | NavigableSet<K> keySet() Trả về chế độ xem NavigableSet của các khóa có trong bản đồ này. |
6 | NavigableSet<K> navigableKeySet() Trả về chế độ xem NavigableSet của các khóa có trong bản đồ này. |
7 | ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) Trả về chế độ xem một phần của bản đồ này có các khóa nằm trong khoảng từ Khóa đến Khóa. |
số 8 | ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey) Trả về chế độ xem một phần của bản đồ này có các khóa từ Khóa, bao gồm, đến Khóa, dành riêng. |
9 | ConcurrentNavigableMap<K,V> tailMap(K fromKey) Trả về chế độ xem của một phần bản đồ này có các khóa lớn hơn hoặc bằng fromKey. |
10 | ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive) Trả về chế độ xem phần của bản đồ này có các khóa lớn hơn (hoặc bằng, nếu tính cả là đúng) từKey. |
Thí dụ
Chương trình TestThread sau đây cho thấy cách sử dụng 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"));
}
}
Điều này sẽ tạo ra kết quả sau.
Đầu ra
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}