Chờ Luồng
(Waiting Thread)
Phương thức join() cho phép một luồng chờ cho đến khi một luồng khác hoàn thành. Khi bạn gọi phương thức join() trên một đối tượng Thread, luồng hiện tại sẽ tạm dừng thực thi cho đến khi luồng của đối tượng Thread đó kết thúc.
Tuy nhiên, giống như phương thức sleep(), join cũng phụ thuộc vào hệ điều hành để tính thời gian, do đó, bạn không nên giả định rằng join sẽ chờ chính xác đúng như khoảng thời gian bạn chỉ định.
Phương thức join() có một vài phiên bản khác nhau, mỗi phiên bản cho phép bạn điều khiển thời gian chờ đợi của luồng một cách linh hoạt hơn. Dưới đây là 3 phiên bản khác nhau của phương thức join():
1. Phương thức void join()
↳ Khi phương thức join() được gọi, luồng hiện tại dừng thực thi và chuyển sang trạng thái chờ. Luồng hiện tại vẫn ở trạng thái chờ cho đến khi luồng mà phương thức join() được gọi kết thúc. Nếu luồng bị gián đoạn, nó sẽ ném ra ngoại lệ InterruptedException.
Cú pháp:
Cú pháp
public final void join() throws InterruptedException
Dưới đây là một ví dụ về cách sử dụng phương thức join() trong Java để chờ đợi một luồng khác kết thúc trước khi tiếp tục thực thi:
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
// Tạo hai đối tượng luồng
Thread thread1 = new Thread(new SimpleTask("Task 1"));
Thread thread2 = new Thread(new SimpleTask("Task 2"));
// Bắt đầu thực thi hai luồng
thread1.start();
thread2.start();
try {
// Chờ đợi cho hai luồng hoàn thành
thread1.join(); // Luồng chính chờ cho thread1 hoàn thành
System.out.println("Thread 1 đã hoàn tất.");
thread2.join(); // Luồng chính chờ cho thread2 hoàn thành
System.out.println("Thread 2 đã hoàn tất.");
} catch (InterruptedException e) {
System.out.println("Thread main đã bị gián đoạn!!");
}
// Luồng chính tiếp tục thực hiện sau khi cả hai luồng hoàn thành
System.out.println("Cả hai Thread đã hoàn tất. Thread main tiếp tục.");
}
}
class SimpleTask implements Runnable {
private String taskName;
public SimpleTask(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(taskName + " đang hoạt động: " + i);
try {
Thread.sleep(1000); // Dừng lại 1 giây
} catch (InterruptedException e) {
System.out.println(taskName + " đã bị gián đoạn!");
}
}
}
}
Kết quả của chương trình là
Task 2 đang hoạt động: 1
Task 2 đang hoạt động: 2
Task 1 đang hoạt động: 2
Task 1 đang hoạt động: 3
Task 2 đang hoạt động: 3
Thread 1 đã hoàn tất.
Thread 2 đã hoàn tất.
Cả hai Thread đã hoàn tất. Thread main tiếp tục.
Ví dụ trên minh họa cách sử dụng phương thức join() để đảm bảo rằng luồng chính sẽ không tiếp tục thực hiện công việc của nó cho đến khi luồng phụ đã hoàn thành công việc của nó.
2. Phương thức join(long millis)
↳ Khi phương thức join() được gọi, luồng hiện tại dừng thực thi và chuyển sang trạng thái chờ.
↳ Luồng hiện tại vẫn ở trạng thái chờ cho đến khi luồng mà phương thức join() được gọi kết thúc hoặc thời gian chờ đợi đã định (tính bằng mili giây) kết thúc.
↳ Nếu luồng bị gián đoạn, nó sẽ ném ra ngoại lệ InterruptedException.
Cú pháp:
Cú pháp
public final void join() throws InterruptedException
Dưới đây là một ví dụ về cách sử dụng phương thức join(long millis) để chờ đợi một khoảng thời gian nhất định trước khi tiếp tục thực hiện. Trong ví dụ này, chúng ta sẽ có ba luồng, và luồng chính sẽ chờ cho một khoảng thời gian cụ thể trước khi tiếp tục thực hiện.
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
// Tạo ba đối tượng luồng
Thread thread1 = new Thread(new SimpleTask("Task 1"));
Thread thread2 = new Thread(new SimpleTask("Task 2"));
Thread thread3 = new Thread(new SimpleTask("Task 3"));
// Bắt đầu thực thi các luồng
thread1.start();
thread2.start();
thread3.start();
try {
// Chờ đợi tối đa 2000 mili giây (2 giây) cho mỗi luồng
thread1.join(2000); // Chờ tối đa 2 giây cho thread1 hoàn thành
System.out.println("Thread 1 đã hoàn thành hoặc hết thời gian chờ..");
thread2.join(2000); // Chờ tối đa 2 giây cho thread2 hoàn thành
System.out.println("Thread 2 đã hoàn thành hoặc hết thời gian chờ..");
thread3.join(2000); // Chờ tối đa 2 giây cho thread3 hoàn thành
System.out.println("Thread 3 đã hoàn thành hoặc hết thời gian chờ.");
} catch (InterruptedException e) {
System.out.println("Main thread đã bị gián đoạn!");
}
// Luồng chính tiếp tục thực hiện sau khi thời gian chờ hết hạn hoặc các luồng đã hoàn thành
System.out.println("Main thread tiếp tục sau khi hết thời gian chờ hoặc luồng hoàn thành..");
}
}
class SimpleTask implements Runnable {
private String taskName;
public SimpleTask(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(taskName + " đang hoạt động: " + i);
try {
Thread.sleep(1000); // Dừng lại 1 giây
} catch (InterruptedException e) {
System.out.println(taskName + " đã bị gián đoạn!");
return; // Thoát khỏi luồng nếu bị gián đoạn
}
}
}
}
Kết quả của chương trình là
Task 2 đang hoạt động: 1
Task 1 đang hoạt động: 1
Task 3 đang hoạt động: 2
Task 1 đang hoạt động: 2
Task 2 đang hoạt động: 2
Thread 1 đã hoàn thành hoặc hết thời gian chờ..
Task 1 đang hoạt động: 3
Task 3 đang hoạt động: 3
Task 2 đang hoạt động: 3
Task 1 đang hoạt động: 4
Task 3 đang hoạt động: 4
Task 2 đang hoạt động: 4
Thread 2 đã hoàn thành hoặc hết thời gian chờ..
Task 1 đang hoạt động: 5
Task 2 đang hoạt động: 5
Task 3 đang hoạt động: 5
Thread 3 đã hoàn thành hoặc hết thời gian chờ.
Main thread tiếp tục sau khi hết thời gian chờ hoặc luồng hoàn thành..
Ví dụ này cho thấy cách sử dụng phương thức join(long millis) để chờ đợi một khoảng thời gian nhất định và xử lý tình huống khi các luồng có thể không hoàn thành trong thời gian chờ đợi.
3. Phương thức join(long mls, int nanos)
↳ Khi phương thức join() được gọi, luồng hiện tại dừng thực thi và chuyển sang trạng thái chờ.
↳ Luồng hiện tại vẫn ở trạng thái chờ cho đến khi luồng mà phương thức join() được gọi kết thúc hoặc thời gian chờ đợi đã định (tính bằng mili giây + nano giây) kết thúc.
↳ nanos: Thời gian chờ đợi thêm tính bằng nano giây (0 đến 999999).
↳ Nếu luồng bị gián đoạn, nó sẽ ném ra ngoại lệ InterruptedException.
Cú pháp:
Cú pháp
public final synchronized void join(long mls, int nanos) throws InterruptedException
Dưới đây là một ví dụ đơn giản về cách sử dụng phương thức join(long millis, int nanos) để chờ đợi một khoảng thời gian chính xác trước khi tiếp tục thực hiện.
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
// Tạo ba đối tượng luồng
Thread thread1 = new Thread(new SimpleTask("Task 1"));
Thread thread2 = new Thread(new SimpleTask("Task 2"));
Thread thread3 = new Thread(new SimpleTask("Task 3"));
// Bắt đầu thực thi các luồng
thread1.start();
thread2.start();
thread3.start();
try {
// Chờ đợi tối đa 2 giây và 500 triệu nano giây (0.5 giây) cho mỗi luồng
thread1.join(2000, 500000); // Chờ tối đa 2 giây và 0.5 mili giây cho thread1 hoàn thành
System.out.println("Thread 1 đã hoàn thành hoặc hết thời gian chờ.");
thread2.join(2000, 500000); // Chờ tối đa 2 giây và 0.5 mili giây cho thread2 hoàn thành
System.out.println("Thread 2 đã hoàn thành hoặc hết thời gian chờ.");
thread3.join(2000, 500000); // Chờ tối đa 2 giây và 0.5 mili giây cho thread3 hoàn thành
System.out.println("Thread 3 đã hoàn thành hoặc hết thời gian chờ.");
} catch (InterruptedException e) {
System.out.println("Main thread đã bị gián đoạn!");
}
// Luồng chính tiếp tục thực hiện sau khi thời gian chờ hết hạn hoặc các luồng đã hoàn thành
System.out.println("Main thread tiếp tục sau khi hết thời gian chờ hoặc luồng hoàn thành...");
}
}
class SimpleTask implements Runnable {
private String taskName;
public SimpleTask(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(taskName + " đang hoạt động: " + i);
try {
Thread.sleep(1000); // Dừng lại 1 giây
} catch (InterruptedException e) {
System.out.println(taskName + " đã bị gián đoạn!");
return; // Thoát khỏi luồng nếu bị gián đoạn
}
}
}
}
Kết quả của chương trình là
Task 2 đang hoạt động: 1
Task 1 đang hoạt động: 1
Task 3 đang hoạt động: 2
Task 2 đang hoạt động: 2
Task 1 đang hoạt động: 2
Thread 1 đã hoàn thành hoặc hết thời gian chờ.
Task 3 đang hoạt động: 3
Task 2 đang hoạt động: 3
Task 1 đang hoạt động: 3
Task 3 đang hoạt động: 4
Task 1 đang hoạt động: 4
Task 2 đang hoạt động: 4
Thread 2 đã hoàn thành hoặc hết thời gian chờ.
Task 3 đang hoạt động: 5
Task 2 đang hoạt động: 5
Task 1 đang hoạt động: 5
Thread 3 đã hoàn thành hoặc hết thời gian chờ.
Main thread tiếp tục sau khi hết thời gian chờ hoặc luồng hoàn thành...
Ví dụ này cho thấy cách sử dụng phương thức join(long millis, int nanos) để chờ đợi một khoảng thời gian chính xác hơn và xử lý tình huống khi các luồng có thể không hoàn thành trong thời gian chờ đợi.
Ví dụ
Dưới đây là ví dụ về cách tạo một luồng khác để gián đoạn luồng chính khi nó đang chờ một luồng khác hoàn thành, từ đó bạn có thể thấy InterruptedException được ném ra.
Ví dụ: Example.java
class InterruptTask extends Thread {
private Thread threadToInterrupt;
// Gán luồng cần gián đoạn trong phương thức này
public void setThreadToInterrupt(Thread thread) {
this.threadToInterrupt = thread;
}
@Override
public void run() {
// Gián đoạn luồng được chỉ định
threadToInterrupt.interrupt();
}
}
public class Example {
public static void main(String[] args) {
try {
// Tạo một đối tượng luồng phụ
Thread mainThread = Thread.currentThread();
// Tạo đối tượng của luồng InterruptTask
InterruptTask interruptTask = new InterruptTask();
interruptTask.setThreadToInterrupt(mainThread); // Gán luồng chính để gián đoạn
interruptTask.start(); // Bắt đầu luồng InterruptTask
// Chờ luồng InterruptTask hoàn thành hoặc bị gián đoạn
interruptTask.join();
} catch (InterruptedException ex) {
System.out.println("Luồng chính đã bị gián đoạn! Ngoại lệ: " + ex);
}
System.out.println("Luồng chính tiếp tục sau khi luồng InterruptTask hoàn thành hoặc bị gián đoạn.");
}
}
Kết quả của chương trình là
Luồng chính tiếp tục sau khi luồng InterruptTask hoàn thành hoặc bị gián đoạn.
Hy vọng rằng qua các ví dụ trên, bạn sẽ hiểu rõ hơn về cách ngắt luồng trong Java.