Java - Tạo một luồng (Thread)
Xin chào các bạn tương lai của các pháp sư Java! Hôm nay, chúng ta sẽ cùng nhau lặn vào thế giới đầy thú vị của các luồng Java. Đừng lo lắng nếu bạn là người mới bắt đầu lập trình; tôi sẽ dẫn dắt bạn từng bước trong hành trình này, giống như tôi đã làm cho biết bao sinh viên trong những năm dạy học của mình. Vậy, hãy lấy饮料 yêu thích của bạn, ngồi thoải mái, và cùng nhau bắt đầu cuộc phiêu lưu về luồng (thread) này nhé!
Luồng (Thread) là gì?
Trước khi chúng ta nhảy vào tạo luồng, hãy hiểu xem luồng thực sự là gì. Hãy tưởng tượng bạn đang trong nhà bếp, nấu một bữa ăn ngon. Bạn đang băm rau, khuấy nước sốt và kiểm tra lò nướng tất cả cùng một lúc. Mỗi nhiệm vụ này giống như một luồng trong chương trình máy tính - chúng là các nhiệm vụ riêng biệt có thể chạy đồng thời.
Trong Java, một luồng là đơn vị thực thi nhỏ nhất trong một chương trình. Nó cho phép các phần khác nhau của chương trình của bạn chạy cùng một lúc, giúp ứng dụng của bạn hiệu quả và nhạy bén hơn.
Tại sao sử dụng Luồng (Thread)?
Bạn có thể tự hỏi, "Tại sao tôi phải phiền lòng với các luồng?" Được rồi, để tôi kể cho bạn một câu chuyện ngắn. Năm xưa, tôi đang phát triển một ứng dụng xử lý ảnh đơn giản cho một câu lạc bộ nhiếp ảnh. Ứng dụng hoạt động tốt cho các hình ảnh nhỏ, nhưng khi người dùng cố gắng xử lý các ảnh có độ phân giải cao, nó sẽ bị冻住. Nguyên nhân là gì? Mọi thứ đều chạy trên một luồng duy nhất! Bằng cách triển khai đa luồng, chúng tôi có thể xử lý ảnh trong nền trong khi giữ giao diện người dùng phản hồi. Các thành viên câu lạc bộ nhiếp ảnh rất hài lòng, và tôi đã học được một bài học quý giá về sức mạnh của các luồng.
Tạo một Luồng trong Java
Trong Java, có hai cách chính để tạo một luồng:
- Thực hiện giao diện Runnable
- Mở rộng lớp Thread
Hãy cùng khám phá cả hai phương pháp với một số ví dụ thực hành!
Phương pháp 1: Thực hiện giao diện Runnable
Đây thường được coi là cách ưu tiên để tạo một luồng vì nó không yêu cầu bạn phải mở rộng lớp Thread, cho phép lớp của bạn mở rộng các lớp khác nếu cần.
Dưới đây là một ví dụ đơn giản:
public class MyRunnable implements Runnable {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Luồng sử dụng Runnable: " + i);
try {
Thread.sleep(1000); // Dừng lại trong 1 giây
} catch (InterruptedException e) {
System.out.println("Luồng bị gián đoạn.");
}
}
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
// Luồng chính
for (int i = 1; i <= 5; i++) {
System.out.println("Luồng chính: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Luồng chính bị gián đoạn.");
}
}
}
}
Hãy phân tích này:
- Chúng ta tạo một lớp
MyRunnable
triển khai giao diệnRunnable
. - Chúng ta ghi đè phương thức
run()
, xác định điều mà luồng sẽ làm khi bắt đầu. - Trong phương thức
main
, chúng ta tạo một đối tượngMyRunnable
và truyền nó cho một đối tượngThread
. - Chúng ta gọi
start()
trên luồng để bắt đầu thực thi. - Luồng chính cũng in số, minh họa cách cả hai luồng chạy đồng thời.
Khi bạn chạy chương trình này, bạn sẽ thấy các số từ cả hai luồng xen kẽ, cho thấy rằng chúng chạy cùng một lúc!
Phương pháp 2: Mở rộng lớp Thread
Phương pháp này liên quan đến việc tạo một lớp con của Thread
và ghi đè phương thức run()
. Dưới đây là một ví dụ:
public class MyThread extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Luồng mở rộng lớp Thread: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Luồng bị gián đoạn.");
}
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
// Luồng chính
for (int i = 1; i <= 5; i++) {
System.out.println("Luồng chính: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Luồng chính bị gián đoạn.");
}
}
}
}
Các khác biệt chính ở đây là:
- Lớp
MyThread
của chúng ta mở rộngThread
thay vì triển khaiRunnable
. - Chúng ta có thể trực tiếp gọi
start()
trên đối tượngMyThread
, mà không cần tạo một đối tượngThread
riêng biệt.
So sánh hai phương pháp
Cả hai phương pháp đều đạt được cùng kết quả, nhưng có một số xem xét:
Tính năng | Giao diện Runnable | Mở rộng lớp Thread |
---|---|---|
Độ linh hoạt | Có thể mở rộng các lớp khác | Không thể mở rộng các lớp khác (Java không hỗ trợ đa kế thừa) |
Tách biệt các mối quan tâm | Tách biệt nhiệm vụ và luồng | Kết hợp nhiệm vụ và luồng trong một lớp |
Khả năng tái sử dụng | Tái sử dụng hơn | Ít tái sử dụng hơn |
Hiệu quả tài nguyên | Hiệu quả hơn (có thể sử dụng với các pool luồng) | Ít hiệu quả hơn |
Trong hầu hết các trường hợp, việc triển khai Runnable
được coi là thực hành tốt hơn vì nó cung cấp nhiều độ linh hoạt hơn và phù hợp hơn với các nguyên tắc thiết kế hướng đối tượng.
Kết luận
Chúc mừng! Bạn đã chính thức bước vào thế giới của lập trình đa luồng trong Java. Chúng ta đã bao gồm các khái niệm cơ bản về luồng là gì, tại sao chúng hữu ích và cách tạo chúng bằng hai phương pháp khác nhau. Nhớ rằng, giống như học đánh trống, việc thành thạo các luồng đòi hỏi sự luyện tập. Đừng nản lòng nếu nó không ngay lập tức hiểu rõ - tiếp tục thử nghiệm và bạn sẽ đến đích!
Trong bài giảng tiếp theo, chúng ta sẽ đi sâu hơn vào việc đồng bộ hóa luồng và giao tiếp giữa các luồng. Đến那时候, chúc bạn lập trình vui vẻ và các luồng của bạn luôn chạy mượt mà!
Credits: Image by storyset