### 1. Lớp Thread trong Java
Lớp `Thread` trong Java là một phần của gói `java.lang`. Nó được sử dụng để tạo và quản lý các luồng. Một luồng (thread) là một đơn vị nhỏ của quá trình xử lý, cho phép thực hiện đa nhiệm đồng thời.
### 2. Tạo Threads
Có hai cách để tạo một luồng mới:
- **Kế thừa từ lớp `Thread`**: Tạo một lớp con của `Thread` và ghi đè phương thức `run()`.
- **Triển khai interface `Runnable`**: Tạo một lớp triển khai interface `Runnable` và triển khai phương thức `run()`.
#### Ví Dụ:
```java
// Kế thừa từ lớp Thread
class MyThread extends Thread {
public void run() {
// Code chạy trong luồng
System.out.println("Luồng đang chạy.");
}
}
// Triển khai interface Runnable
class MyRunnable implements Runnable {
public void run() {
// Code chạy trong luồng
System.out.println("Luồng đang chạy.");
}
}
```
### 3. Các Trạng Thái của Thread (Thread States)
Một luồng trong Java có thể ở một trong năm trạng thái sau:
1. **New**: Trạng thái sau khi tạo thread nhưng trước khi gọi `start()`.
2. **Runnable**: Trạng thái sau khi đã gọi `start()`, thread có thể đang chạy hoặc đợi lịch trình hệ thống.
3. **Blocked**: Trạng thái khi thread đang chờ một khóa (lock) để tiếp tục thực hiện.
4. **Waiting**: Trạng thái khi thread đợi một thread khác thực hiện một hành động cụ thể.
5. **Timed Waiting**: Tương tự như Waiting nhưng có thời gian chờ xác định.
6. **Terminated**: Trạng thái khi thread đã hoàn thành công việc hoặc bị dừng.
### 4. Các Phương Thức của Lớp Thread
Một số phương thức quan trọng của lớp `Thread`:
- `start()`: Khởi động một luồng mới.
- `run()`: Định nghĩa công việc mà luồng sẽ thực hiện.
- `sleep(long millis)`: Tạm dừng luồng trong một khoảng thời gian.
- `join()`: Đợi cho đến khi luồng khác hoàn thành công việc.
- `interrupt()`: Yêu cầu ngắt luồng.
- `isAlive()`: Kiểm tra xem luồng còn hoạt động hay không.
### 5. Quản Lý Threads
Quản lý các luồng (threads) trong Java, đặc biệt là trong bối cảnh của tài nguyên chia sẻ, đòi hỏi sự cẩn trọng để tránh xung đột và tình trạng "race condition". Ba khái niệm chính để quản lý luồng bao gồm `wait()`, `notify()`, và `synchronized`.
#### 1. Synchronized
Mệnh đề `synchronized` được sử dụng để đảm bảo rằng chỉ một luồng có thể truy cập vào một đoạn mã cụ thể tại một thời điểm. Nó giúp tránh xung đột khi các luồng cùng truy cập và thao tác với dữ liệu chia sẻ.
##### Ví Dụ:
```java
class Counter {
private int count = 0;
public synchronized void increment() {
count++; // Chỉ một luồng có thể thực hiện tăng count tại một thời điểm
}
public int getCount() {
return count;
}
}
```
#### 2. Wait và Notify
`wait()` và `notify()` là hai phương thức của lớp Object được sử dụng để giao tiếp giữa các luồng. `wait()` làm cho luồng hiện tại dừng lại và chờ đợi cho đến khi một luồng khác gọi `notify()` hoặc `notifyAll()` trên cùng một đối tượng.
##### Ví Dụ:
```java
class Message {
private String msg;
public synchronized void put(String msg) {
this.msg = msg;
notify(); // Thông báo cho luồng đang chờ rằng dữ liệu đã sẵn sàng
}
public synchronized String take() {
while (msg == null) {
try {
wait(); // Chờ đợi cho đến khi msg được cập nhật
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String returnValue = msg;
msg = null;
return returnValue;
}
}
class Producer implements Runnable {
private Message message;
public Producer(Message message) {
this.message = message;
}
public void run() {
String[] messages = {"msg1", "msg2", "msg3"};
for (String m : messages) {
message.put(m);
}
}
}
class Consumer implements Runnable {
private Message message;
public Consumer(Message message) {
this.message = message;
}
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Received: " + message.take());
}
}
}
public class Main {
public static void main(String[] args) {
Message message = new Message();
new Thread(new Producer(message)).start();
new Thread(new Consumer(message)).start();
}
}
```
Trong ví dụ trên, chúng ta có hai lớp, `Producer` và `Consumer`, cùng làm việc với một đối tượng `Message`. `Producer` đặt các tin nhắn vào `Message`, và `Consumer` nhận các tin nhắn từ `Message`. Việc sử dụng `wait()` và `notify()` đảm bảo rằng `Consumer` chờ đợi cho đến khi có tin nhắn mới và `Producer` thông báo sau khi đặt một tin nhắn.
Quản lý thread hiệu quả thông qua các kỹ thuật như `synchronized`, `wait`, và `notify` là chìa khóa để xây dựng các ứng dụng đa luồng mạnh mẽ và đáng tin cậy trong Java.
### 6. Daemon Threads
Daemon threads là những luồng nền chạy độc lập và không ngăn chương trình chính kết thúc. Khi tất cả các luồng không phải daemon đã kết thú
c, chương trình sẽ kết thúc mà không cần chờ các daemon thread hoàn thành.
Để đặt một luồng thành daemon, sử dụng phương thức `setDaemon(true)` trước khi gọi `start()`.
#### Ví Dụ:
```java
MyThread t = new MyThread();
t.setDaemon(true); // Đặt luồng là daemon
t.start();
```
Dưới đây là phần mở rộng của tài liệu về lập trình đa luồng trong Java, bao gồm ví dụ cụ thể về các trạng thái của Thread và các phương thức của lớp `Thread`.
### Ví Dụ Về Các Trạng Thái của Thread
```java
class ThreadStateExample extends Thread {
public void run() {
try {
// Luồng chuyển sang trạng thái Sleeping
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Luồng đang chạy.");
try {
// Luồng chuyển sang trạng thái Waiting
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String args[]) throws Exception {
ThreadStateExample t1 = new ThreadStateExample();
System.out.println("Trạng thái t1 sau khi tạo: " + t1.getState());
t1.start();
System.out.println("Trạng thái t1 sau khi gọi start(): " + t1.getState());
// Cho chút thời gian để t1 chuyển sang trạng thái Sleeping
Thread.sleep(200);
System.out.println("Trạng thái t1 sau khi chuyển sang Sleeping: " + t1.getState());
// Chờ đến khi t1 hoàn thành
t1.join();
System.out.println("Trạng thái t1 sau khi kết thúc: " + t1.getState());
}
}
```
### Ví Dụ Về Các Phương Thức Của Lớp Thread
1. **start()**:
```java
Thread thread = new Thread(new MyRunnable());
thread.start(); // Khởi động thread mới
```
2. **run()**:
```java
@Override
public void run() {
// Định nghĩa công việc cần thực hiện
}
```
3. **sleep(long millis)**:
```java
public void run() {
try {
Thread.sleep(1000); // Tạm dừng thread trong 1 giây
} catch (InterruptedException e) {
e.printStackTrace();
}
}
```
4. **join()**:
```java
Thread t1 = new Thread(new MyRunnable());
t1.start();
t1.join(); // Chờ đến khi t1 hoàn thành
```
5. **interrupt()**:
```java
Thread t1 = new Thread(new MyRunnable());
t1.start();
t1.interrupt(); // Ngắt luồng t1
```
6. **isAlive()**:
```java
Thread t1 = new Thread(new MyRunnable());
t1.start();
System.out.println(t1.isAlive()); // Kiểm tra t1 còn hoạt động hay không
```
### Ví Dụ Về Daemon Thread
```java
public class DaemonThreadExample extends Thread {
public void run() {
if(Thread.currentThread().isDaemon()){ // Kiểm tra xem có phải là Daemon thread không
System.out.println("Daemon thread chạy.");
}
else {
System.out.println("User thread chạy.");
}
}
public static void main(String[] args) {
DaemonThreadExample t1 = new DaemonThreadExample();
DaemonThreadExample t2 = new DaemonThreadExample();
t1.setDaemon(true); // Đặt t1 là Daemon thread
t1.start(); // Khởi động t1
t2.start(); // Khởi động t2
}
}
```
Dựa trên các thông tin bạn cung cấp, tôi sẽ tạo một tài liệu giảng dạy về đa luồng trong Java. Tài liệu này sẽ bao gồm các khái niệm cơ bản và các phương thức quan trọng liên quan đến đa luồng.
---
## Giáo Trình Đa Luồng trong Java
### I. Khái Niệm Cơ Bản
- **Đa Luồng (Multithreading)**: Là khả năng thực thi đồng thời nhiều luồng trong một ứng dụng. Điều này giúp tăng hiệu suất và quản lý tài nguyên hiệu quả hơn.
### II. Các Phương Thức Quan Trọng
1. **Phương thức isAlive()**
- Mục Đích: Kiểm tra xem luồng có đang trong trạng thái khả thi, đang chạy, hay đã kết thúc.
2. **Phương thức join()**
- Mục Đích: Buộc một luồng đang chạy phải đợi cho đến khi luồng khác hoàn thành nhiệm vụ của mình.
3. **Khối Đồng Bộ (Synchronized Block)**
- Mục Đích: Tránh tình trạng đua (Race Condition) bằng cách đồng bộ hóa các khối lệnh.
4. **Phương thức wait()**
- Mục Đích: Gửi luồng đang chạy ra khỏi khóa hoặc monitor để chờ đợi.
5. **Phương thức notify()**
- Mục Đích: Hướng dẫn một luồng đang chờ vào khóa của đối tượng mà nó đang chờ đợi.
6. **Deadlock**
- Khái Niệm: Mô tả tình huống hai hoặc nhiều luồng bị chặn mãi mãi, chờ đợi lẫn nhau để giải phóng tài nguyên.
### III. Công Cụ StackWalker trong Java 9
- **Giới Thiệu StackWalker API**
- Java 9 giới thiệu StackWalker API, cung cấp khả năng duyệt qua stack và hỗ trợ truy tìm nguyên nhân gốc rễ của ngoại lệ.
- Lớp Chính: StackWalker
Có, bạn hoàn toàn có thể tạo ra một `ArrayList` của các luồng với ví dụ trên và thực hiện chúng đồng thời. Sau đó, bạn có thể chờ đợi cho đến khi tất cả các luồng hoàn thành công việc của mình và hiển thị thông báo. Dưới đây là cách làm:
### Ví Dụ: Tạo ArrayList của Luồng và Thực Hiện Đồng Thời
**Bước 1:** Sử dụng lại lớp `FileReaderThread` từ ví dụ trước.
**Bước 2:** Tạo lớp chính để quản lý và chạy các luồng.
```java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<FileReaderThread> threads = new ArrayList<>();
threads.add(new FileReaderThread("file1.txt"));
threads.add(new FileReaderThread("file2.txt"));
threads.add(new FileReaderThread("file3.txt"));
// Bắt đầu thực thi tất cả các luồng
for (FileReaderThread thread : threads) {
thread.start();
}
// Chờ đợi tất cả các luồng hoàn thành
for (FileReaderThread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Hiển thị thông báo sau khi tất cả luồng hoàn thành
System.out.println("Tất cả các luồng đã hoàn thành việc đọc file.");
}
}
```
Trong đoạn mã này, bạn tạo một danh sách các `FileReaderThread` và khởi chạy từng luồng bằng cách gọi `thread.start()`. Sau đó, bạn sử dụng `thread.join()` để chờ đợi mỗi luồng hoàn thành công việc của mình. Khi tất cả các luồng đã hoàn thành, chương trình sẽ in ra thông báo "Tất cả các luồng đã hoàn thành việc đọc file."