Thực Hiện Nhiều Tác Vụ
(Perform Multiple Tasks)
Trong lập trình, việc thực hiện nhiều nhiệm vụ cùng một lúc là một phương pháp quan trọng để giúp các ứng dụng hoạt động nhanh hơn và hiệu quả hơn. Khi bạn có thể thực hiện nhiều công việc đồng thời, chương trình của bạn có thể xử lý nhiều yêu cầu cùng lúc, như tải dữ liệu, xử lý thông tin, và tương tác với người dùng mà không bị chậm trễ. Điều này không chỉ cải thiện hiệu suất mà còn mang lại trải nghiệm mượt mà hơn cho người dùng và sử dụng tối đa khả năng của máy tính.
1. Thực hiện một tác vụ duy nhất bằng nhiều luồng (Perform a single task using multiple threads)
Để thực hiện một tác vụ duy nhất bằng nhiều luồng (threads) trong Java, bạn có thể tạo một lớp thực hiện giao diện Runnable hoặc mở rộng lớp Thread, và định nghĩa phương thức run() nơi mà tác vụ được thực hiện. Sau đó, bạn tạo nhiều luồng để thực hiện phương thức run() này đồng thời.
Ví dụ 1:
Dưới đây là ví dụ về cách thực hiện một tác vụ duy nhất bằng nhiều luồng trong Java. Trong trường hợp này, chúng ta sẽ sử dụng Runnable để triển khai một tác vụ và tạo nhiều luồng để thực thi cùng một tác vụ đó:
Ví dụ: Example.java
class MyTask implements Runnable {
public void run() {
System.out.println("Tác vụ đang được thực hiện bởi luồng: " + Thread.currentThread().getName());
}
}
public class Example {
public static void main(String[] args) {
MyTask task = new MyTask(); // Một tác vụ duy nhất
// Tạo nhiều luồng để thực hiện tác vụ duy nhất
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
Thread thread3 = new Thread(task);
// Khởi động các luồng
thread1.start();
thread2.start();
thread3.start();
}
}
Kết quả của chương trình là
Tác vụ đang được thực hiện bởi luồng: Thread-2
Tác vụ đang được thực hiện bởi luồng: Thread-0
Kết quả đầu ra cho thấy cùng một tác vụ đang được thực hiện bởi nhiều luồng cùng lúc. Thứ tự thực hiện có thể thay đổi mỗi lần chương trình chạy, vì việc lập lịch luồng được quản lý bởi JVM và hệ điều hành nền tảng.
Ví dụ 2:
Dưới đây là một ví dụ về chương trình thực hiện một tác vụ duy nhất bằng nhiều luồng bằng cách kế thừa từ lớp Thread:
Ví dụ: Example.java
class MyTask extends Thread {
public void run() {
System.out.println("Tác vụ đang được thực hiện bởi luồng: " + Thread.currentThread().getName());
}
}
public class Example {
public static void main(String[] args) {
// Tạo các đối tượng MyTask (mỗi đối tượng là một luồng)
MyTask thread1 = new MyTask();
MyTask thread2 = new MyTask();
MyTask thread3 = new MyTask();
// Khởi động các luồng
thread1.start();
thread2.start();
thread3.start();
}
}
Kết quả của chương trình là
Tác vụ đang được thực hiện bởi luồng: Thread-2
Tác vụ đang được thực hiện bởi luồng: Thread-1
Kết quả này tương tự như khi bạn sử dụng giao diện Runnable, nhưng ở đây bạn đang kế thừa trực tiếp từ lớp Thread. Mỗi lần chương trình chạy, thứ tự thực hiện các luồng có thể thay đổi, vì việc lập lịch luồng được quản lý bởi JVM và hệ điều hành.
Lưu ý: Mỗi luồng chạy trong một callstack (ngăn xếp lệnh) riêng biệt. Điều này có nghĩa là mỗi luồng có không gian riêng để thực thi các lệnh và không bị can thiệp bởi các luồng khác. Mỗi luồng sẽ thực hiện các tác vụ của mình một cách độc lập, với ngăn xếp lệnh riêng, giúp đảm bảo rằng chúng không xung đột với nhau khi thực thi.
2. Thực hiện nhiều tác vụ bằng nhiều luồng (Perform multiple tasks using multiple threads)
Để thực hiện nhiều tác vụ bằng nhiều luồng, bạn cần tạo nhiều lớp (hoặc đối tượng) mỗi lớp (hoặc đối tượng) sẽ thực hiện một tác vụ khác nhau. Mỗi lớp này sẽ kế thừa lớp Thread hoặc triển khai giao diện Runnable và định nghĩa phương thức run() để thực hiện tác vụ của nó.
Ví dụ 1:
Dưới đây là ví dụ về cách thực hiện hai tác vụ khác nhau bằng hai luồng bằng cách kế thừa từ lớp Thread trong Java:
Ví dụ: Example.java
// Lớp thực hiện tác vụ đầu tiên
class Task1 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task 1 - Count: " + i);
try {
Thread.sleep(500); // Tạm dừng luồng trong 500ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// Lớp thực hiện tác vụ thứ hai
class Task2 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task 2 - Count: " + i);
try {
Thread.sleep(700); // Tạm dừng luồng trong 700ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// Lớp chính để chạy chương trình
public class Example {
public static void main(String[] args) {
// Tạo các đối tượng luồng cho các tác vụ
Task1 t1 = new Task1();
Task2 t2 = new Task2();
// Khởi động các luồng
t1.start();
t2.start();
}
}
Kết quả của chương trình là
Task 2 - Count: 1
....
Task 2 - Count: 4
Task 2 - Count: 5
Bằng cách định nghĩa nhiều lớp (hoặc đối tượng) với các phương thức run() khác nhau và khởi động chúng bằng các luồng khác nhau, bạn có thể thực hiện nhiều tác vụ đồng thời. Đây là cách để thực hiện multitasking trong môi trường đa luồng (multithreading).
Ví dụ 2:
Dưới đây là ví dụ về cách thực hiện hai tác vụ khác nhau bằng hai luồng trong Java, sử dụng giao diện Runnable thay vì kế thừa từ lớp Thread:
Ví dụ: Example.java
// Lớp thực hiện tác vụ đầu tiên, triển khai giao diện Runnable
class Task1 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task 1 - Count: " + i);
try {
Thread.sleep(500); // Tạm dừng luồng trong 500ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// Lớp thực hiện tác vụ thứ hai, triển khai giao diện Runnable
class Task2 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task 2 - Count: " + i);
try {
Thread.sleep(700); // Tạm dừng luồng trong 700ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// Lớp chính để chạy chương trình
public class Example {
public static void main(String[] args) {
// Tạo các đối tượng Runnable cho các tác vụ
Runnable task1 = new Task1();
Runnable task2 = new Task2();
// Tạo các đối tượng Thread với các đối tượng Runnable
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
// Khởi động các luồng
thread1.start();
thread2.start();
}
}
Kết quả của chương trình là
Task 2 - Count: 1
....
Task 2 - Count: 4
Task 2 - Count: 5
Việc sử dụng giao diện Runnable cho phép bạn tách biệt việc thực hiện tác vụ khỏi việc quản lý luồng. Điều này giúp mã của bạn trở nên dễ bảo trì và linh hoạt hơn, vì bạn có thể triển khai nhiều lớp thực hiện Runnable và quản lý chúng thông qua các đối tượng Thread.
Ví dụ 3:
Dưới đây là ví dụ về cách thực hiện hai tác vụ khác nhau bằng hai luồng trong Java, sử dụng lớp ẩn danh (anonymous class) mở rộng từ lớp Thread:
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
// Tạo và khởi động luồng thực hiện tác vụ đầu tiên
Thread thread1 = new Thread() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task 1 - Count: " + i);
try {
Thread.sleep(500); // Tạm dừng luồng trong 500ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// Tạo và khởi động luồng thực hiện tác vụ thứ hai
Thread thread2 = new Thread() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task 2 - Count: " + i);
try {
Thread.sleep(700); // Tạm dừng luồng trong 700ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// Khởi động các luồng
thread1.start();
thread2.start();
}
}
Kết quả của chương trình là
Task 2 - Count: 1
....
Task 2 - Count: 4
Task 2 - Count: 5
Sử dụng lớp ẩn danh là một cách tiện lợi để định nghĩa các tác vụ cho các luồng mà không cần phải tạo các lớp con riêng biệt. Điều này giúp làm cho mã nguồn ngắn gọn và dễ đọc hơn khi bạn chỉ cần thực hiện các tác vụ đơn giản.
Ví dụ 4:
Dưới đây là ví dụ về cách thực hiện hai tác vụ khác nhau bằng hai luồng trong Java, sử dụng lớp ẩn danh (anonymous class) triển khai giao diện Runnable:
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
// Tạo và khởi động luồng thực hiện tác vụ đầu tiên
Runnable task1 = new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task 1 - Count: " + i);
try {
Thread.sleep(500); // Tạm dừng luồng trong 500ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// Tạo và khởi động luồng thực hiện tác vụ thứ hai
Runnable task2 = new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task 2 - Count: " + i);
try {
Thread.sleep(700); // Tạm dừng luồng trong 700ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// Tạo các đối tượng Thread với các đối tượng Runnable
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
// Khởi động các luồng
thread1.start();
thread2.start();
}
}
Kết quả của chương trình là
Task 2 - Count: 1
....
Task 2 - Count: 4
Task 2 - Count: 5
Sử dụng lớp ẩn danh để triển khai giao diện Runnable là một cách thuận tiện để định nghĩa các tác vụ cho các luồng mà không cần phải định nghĩa lớp con riêng biệt. Điều này giúp mã nguồn trở nên gọn gàng hơn, đặc biệt khi bạn chỉ cần thực hiện các tác vụ đơn giản và không cần tái sử dụng các lớp này.