Đa luồng (Multithreading) trong Java
VI. Đa luồng (Multithreading) trong Java:
A. Khởi tạo và thực thi một luồng:
Trong Java, bạn có thể tạo và thực thi một luồng bằng cách mở rộng lớp Thread
hoặc triển khai giao diện Runnable
. Dưới đây là cách bạn có thể làm điều đó:
-
Kế thừa lớp Thread:
- Bước 1: Tạo một lớp con kế thừa từ lớp
Thread
. - Bước 2: Override phương thức
run()
để xác định công việc mà luồng sẽ thực hiện khi được khởi tạo. - Bước 3: Tạo một đối tượng của lớp con đã tạo và gọi phương thức
start()
để bắt đầu thực thi luồng.
1 2 3 4 5 6 7 8 9 10 11 12
class MyThread extends Thread { public void run() { System.out.println("MyThread is running"); } } public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } }
- Bước 1: Tạo một lớp con kế thừa từ lớp
-
Triển khai giao diện Runnable:
- Bước 1: Tạo một lớp triển khai giao diện
Runnable
. - Bước 2: Override phương thức
run()
để xác định công việc mà luồng sẽ thực hiện khi được khởi tạo. - Bước 3: Tạo một đối tượng của lớp triển khai
Runnable
, sau đó chuyển đối tượng này vào một đối tượng của lớpThread
. - Bước 4: Gọi phương thức
start()
trên đối tượngThread
để bắt đầu thực thi luồng.
1 2 3 4 5 6 7 8 9 10 11 12 13
class MyRunnable implements Runnable { public void run() { System.out.println("MyRunnable is running"); } } public class Main { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); } }
- Bước 1: Tạo một lớp triển khai giao diện
Bằng cách này, bạn có thể khởi tạo và thực thi một luồng trong Java. Điều này cho phép bạn thực hiện các tác vụ song song và tận dụng được khả năng xử lý đa luồng của hệ thống.
B. Synchronization và Deadlock:
-
Synchronization:
- Trong Java, synchronization được sử dụng để đảm bảo rằng chỉ có một luồng có thể truy cập vào một phần của mã tại một thời điểm.
- Điều này là cần thiết khi nhiều luồng cố gắng cập nhật hoặc sử dụng các tài nguyên chia sẻ, như biến toàn cục hoặc phương thức của một đối tượng.
- Bạn có thể sử dụng khối synchronized hoặc từ khóa synchronized để đạt được đồng bộ hóa.
1 2 3
public synchronized void synchronizedMethod() { // Các lệnh được đảm bảo chỉ được thực thi bởi một luồng tại một thời điểm }
-
Deadlock:
- Deadlock là tình trạng khi hai hoặc nhiều luồng bị kẹt trong một vòng lặp vô hạn, vì mỗi luồng đang chờ đợi một tài nguyên mà luồng khác đang giữ.
- Deadlock có thể xảy ra khi một luồng cố gắng giữ một tài nguyên và yêu cầu một tài nguyên khác đã được giữ bởi một luồng khác, và ngược lại.
- Để tránh deadlock, bạn nên sử dụng các phương thức synchronized một cách cẩn thận và tránh việc giữ tài nguyên trong thời gian dài.
Ví dụ về deadlock:
|
|
Trong ví dụ này, thread1 sẽ giữ resource1 và yêu cầu resource2, trong khi thread2 đang giữ resource2 và yêu cầu resource1. Điều này dẫn đến deadlock.
C. Xử lý Exception trong đa luồng:
Xử lý ngoại lệ trong đa luồng đôi khi có thể trở nên phức tạp vì mỗi luồng có thể có riêng một cơ chế xử lý ngoại lệ. Dưới đây là một số cách để xử lý ngoại lệ trong đa luồng trong Java:
-
Xử lý ngoại lệ trong từng luồng:
- Mỗi luồng có thể có một cơ chế xử lý ngoại lệ riêng bằng cách sử dụng khối try-catch trong phương thức
run()
. - Bất kỳ ngoại lệ nào xảy ra trong một luồng sẽ được xử lý bởi khối catch của chính luồng đó.
1 2 3 4 5 6 7 8 9
class MyThread extends Thread { public void run() { try { // Code có thể gây ra ngoại lệ } catch (Exception e) { // Xử lý ngoại lệ ở đây } } }
- Mỗi luồng có thể có một cơ chế xử lý ngoại lệ riêng bằng cách sử dụng khối try-catch trong phương thức
-
Xử lý ngoại lệ toàn cục (Uncaught Exception Handler):
- Bạn có thể thiết lập một bộ xử lý ngoại lệ toàn cục cho tất cả các luồng sử dụng phương thức
setDefaultUncaughtExceptionHandler()
của lớpThread
. - Bộ xử lý này sẽ được gọi mỗi khi một luồng sinh ra ngoại lệ và không có bất kỳ khối try-catch nào để xử lý.
1 2 3 4 5
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { // Xử lý ngoại lệ ở đây } });
- Bạn có thể thiết lập một bộ xử lý ngoại lệ toàn cục cho tất cả các luồng sử dụng phương thức
-
Xử lý ngoại lệ trong ExecutorService:
- Nếu bạn sử dụng
ExecutorService
để quản lý luồng, bạn có thể sử dụng phương thứcsubmit()
để gửi mộtCallable
hoặcRunnable
và sau đó sử dụngFuture
để nhận kết quả hoặc xử lý ngoại lệ.
1 2 3 4 5 6 7 8 9 10
ExecutorService executor = Executors.newFixedThreadPool(5); Future<?> future = executor.submit(() -> { // Code có thể gây ra ngoại lệ }); try { future.get(); // Lấy kết quả hoặc xử lý ngoại lệ } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }
- Nếu bạn sử dụng
Quá trình xử lý ngoại lệ trong đa luồng cần phải cẩn thận để đảm bảo rằng mọi ngoại lệ đều được xử lý một cách an toàn và không gây ra tình trạng không ổn định cho hệ thống.