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é!

Java - Creating a Thread

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:

  1. Thực hiện giao diện Runnable
  2. 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:

  1. Chúng ta tạo một lớp MyRunnable triển khai giao diện Runnable.
  2. Chúng ta ghi đè phương thức run(), xác định điều mà luồng sẽ làm khi bắt đầu.
  3. Trong phương thức main, chúng ta tạo một đối tượng MyRunnable và truyền nó cho một đối tượng Thread.
  4. Chúng ta gọi start() trên luồng để bắt đầu thực thi.
  5. 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à:

  1. Lớp MyThread của chúng ta mở rộng Thread thay vì triển khai Runnable.
  2. Chúng ta có thể trực tiếp gọi start() trên đối tượng MyThread, mà không cần tạo một đối tượng Thread 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