Java - Multithreading: Hướng dẫn cho người mới bắt đầu

Xin chào các bạn đang học lập trình Java! Hôm nay, chúng ta sẽ bắt đầu một hành trình đầy thú vị vào thế giới của Java multithreading. Đừng lo lắng nếu bạn là người mới bắt đầu - tôi sẽ là người hướng dẫn thân thiện của bạn, và chúng ta sẽ cùng nhau giải quyết chủ đề này từng bước một. Vậy, hãy lấy một tách cà phê (hoặc trà, nếu bạn thích), và cùng nhau bắt đầu!

Java - Multithreading

Multithreading là gì?

Hãy tưởng tượng bạn đang ở trong bếp, cố gắng chuẩn bị một bữa ăn phức tạp. Bạn có thể làm mọi thứ một cách tuần tự - băm rau, sau đó đun pasta, rồi chuẩn bị nước sốt. Nhưng liệu có không hiệu quả hơn nếu bạn có thể làm tất cả các nhiệm vụ này cùng một lúc? Đó chính xác là điều mà multithreading cho phép các chương trình của chúng ta làm!

Trong những thuật ngữ đơn giản, multithreading là một tính năng cho phép một chương trình thực hiện nhiều nhiệm vụ cùng một lúc. Mỗi nhiệm vụ này được gọi là một "thread", và chúng chạy độc lập nhưng có thể chia sẻ tài nguyên khi cần thiết.

Tại sao sử dụng Multithreading?

Bạn có thể đang tự hỏi, "Tại sao tôi nên quan tâm đến multithreading?" Hãy để tôi kể cho bạn một câu chuyện ngắn.

Ngày xưa khi tôi mới bắt đầu lập trình, tôi đã tạo một ứng dụng đơn giản để tải xuống nhiều tệp từ internet. Nó hoạt động tốt, nhưng rất chậm vì nó tải xuống một tệp tại một thời điểm. Sau đó, tôi đã học về multithreading, áp dụng nó vào chương trình của mình, và voilà! Nó giống như việc nâng cấp từ xe đạp lên xe thể thao. Các tệp được tải xuống cùng một lúc, và quy trình tổng thể trở nên nhanh hơn nhiều.

Multithreading có thể:

  1. Cải thiện hiệu suất và hiệu quả
  2. Cho phép sử dụng tài nguyên tốt hơn
  3. Cải thiện trải nghiệm người dùng trong các ứng dụng GUI
  4. Cho phép các thao tác không đồng bộ

Vòng đời của một Thread

Trước khi chúng ta bắt đầu lập trình, hãy hiểu về vòng đời của một thread. Nó giống như cuộc sống của một con bướm, nhưng với nhiều lập trình và ít bay lượn hơn!

  1. New: Thread được tạo ra nhưng chưa bắt đầu.
  2. Runnable: Thread sẵn sàng chạy và chờ đợi CPU.
  3. Running: Thread đang thực hiện nhiệm vụ của mình.
  4. Blocked/Waiting: Thread tạm thời không hoạt động (ví dụ: chờ đợi I/O hoặc một thread khác).
  5. Terminated: Thread đã hoàn thành nhiệm vụ và đã chết.

Bây giờ, hãy xem chúng ta có thể tạo và sử dụng thread như thế nào trong Java.

Tạo Thread trong Java

Có hai cách chính để tạo thread trong Java:

1. Thực hiện Interface Runnable

Phương pháp này thường được coi là tốt hơn vì nó không yêu cầu chúng ta phải kế thừa lớp Thread, cho phép lớp của chúng ta kế thừa các lớp khác nếu cần.

public class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running!");
}

public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}

Trong ví dụ này:

  • Chúng ta tạo một lớp MyRunnable implements Runnable.
  • Chúng ta ghi đè phương thức run(), xác định điều thread sẽ làm.
  • Trong phương thức main, chúng ta tạo một thể hiện của MyRunnable và truyền nó cho một đối tượng Thread.
  • Chúng ta gọi phương thức start() để bắt đầu thực thi thread.

2. Kế thừa lớp Thread

Phương pháp này đơn giản nhưng ít linh hoạt hơn.

public class MyThread extends Thread {
public void run() {
System.out.println("Thread is running!");
}

public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}

Ở đây:

  • Chúng ta tạo một lớp MyThread kế thừa Thread.
  • Chúng ta ghi đè phương thức run().
  • Trong phương thức main, chúng ta tạo một thể hiện của MyThread và gọi phương thức start() của nó.

Ưu tiên của Thread

Giống như trong lớp học, nơi một số học sinh được gọi nhiều hơn so với những người khác (không phải tôi bao giờ cũng favoritism!), thread có thể có các ưu tiên khác nhau. Mức ưu tiên dao động từ 1 (thấp nhất) đến 10 (cao nhất), với 5 là mặc định.

public class PriorityDemo {
public static void main(String[] args) {
Thread t1 = new Thread(() -> System.out.println("I'm thread 1"));
Thread t2 = new Thread(() -> System.out.println("I'm thread 2"));

t1.setPriority(Thread.MIN_PRIORITY); // Ưu tiên 1
t2.setPriority(Thread.MAX_PRIORITY); // Ưu tiên 10

t1.start();
t2.start();
}
}

Trong ví dụ này, t2 có ưu tiên cao hơn, vì vậy nó có khả năng chạy trước t1. Tuy nhiên, hãy nhớ rằng việc lịch trình thread có thể không dự đoán được, vì vậy đừng phụ thuộc quá nhiều vào ưu tiên!

Các phương thức quan trọng của Thread

Hãy xem xét một số phương thức quan trọng của lớp Thread:

Phương thức Mô tả
start() Bắt đầu thread, gọi phương thức run()
run() Chứa mã xác định nhiệm vụ của thread
sleep(long millis) Dừng thread trong một khoảng thời gian xác định (theo мили giây)
join() Chờ đợi thread chết
isAlive() Kiểm tra xem thread có còn sống không
interrupt() Ngắt thread

Dưới đây là một ví dụ đơn giản sử dụng một số phương thức này:

public class ThreadMethodsDemo {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread is working: " + i);
try {
Thread.sleep(1000); // Ngủ trong 1 giây
} catch (InterruptedException e) {
System.out.println("Thread was interrupted!");
return;
}
}
});

thread.start();
System.out.println("Thread is alive: " + thread.isAlive());

Thread.sleep(3000); // Main thread ngủ trong 3 giây
thread.interrupt(); // Ngắt thread

thread.join(); // Chờ đợi thread hoàn thành
System.out.println("Thread is alive: " + thread.isAlive());
}
}

Ví dụ này minh họa việc bắt đầu một thread, kiểm tra xem nó có còn sống không, ngủ, ngắt và chờ đợi thread.

Các khái niệm quan trọng về Java Multithreading

Bây giờ chúng ta đã bao gồm các khái niệm cơ bản, hãy điểm qua một số khái niệm advanced về multithreading:

  1. Synchronization: Đảm bảo rằng chỉ một thread có thể truy cập vào một tài nguyên chia sẻ tại một thời điểm.
  2. Deadlock: Tình huống mà hai hoặc nhiều thread không thể tiếp tục vì mỗi thread đang chờ đợi nhau để解锁.
  3. Thread Pool: Nhóm các thread công nhân chờ đợi các nhiệm vụ và có thể được tái sử dụng nhiều lần.
  4. Concurrent Collections: Các bộ sưu tập an toàn với thread được thiết kế để sử dụng trong môi trường đa thread.

Những khái niệm này rất quan trọng cho việc viết các ứng dụng multithreaded hiệu quả và không có lỗi, nhưng chúng là chủ đề cho một ngày khác!

Kết luận

Chúc mừng! Bạn đã迈出了第一步进入Java multithreading. Chúng ta đã bao gồm các khái niệm cơ bản về thread là gì, cách tạo chúng và một số phương thức cơ bản để làm việc với chúng.

Hãy nhớ, multithreading là một công cụ mạnh mẽ, nhưng nó cũng có thể mang lại sự phức tạp và tiềm ẩn lỗi nếu không được sử dụng cẩn thận. Khi bạn tiếp tục hành trình Java của mình, hãy tiếp tục thực hành và khám phá các khái niệm advanced về multithreading.

Chúc các bạn lập trình vui vẻ, và mong rằng các thread của bạn luôn chạy mượt mà!

Credits: Image by storyset