Việc đồng bộ hóa thread là một trong những yếu tố quan trọng trong lập trình đa luồng, đặc biệt trong Java. Một monitor là một cơ chế cho phép chỉ một thread có thể truy cập vào một phần mã nào đó tại một thời điểm. Trong bài viết này, chúng ta sẽ khám phá cách thức hoạt động của việc đồng bộ hóa bên trong một monitor, cùng với các khái niệm, cách sử dụng và ví dụ minh họa.
1. Monitor là gì?
1.1. Định nghĩa
Monitor là một cấu trúc dữ liệu được sử dụng để kiểm soát truy cập vào tài nguyên chia sẻ. Trong Java, mọi đối tượng đều có thể được coi là một monitor, và chúng sử dụng đồng bộ hóa để đảm bảo rằng chỉ một thread có thể truy cập vào một khối mã nhất định tại một thời điểm.
1.2. Tính chất
Monitor có hai tính chất chính:
- Mutual Exclusion: Chỉ một thread có thể thực thi một phần mã bên trong monitor tại một thời điểm.
- Wait: Nếu một thread đang nắm giữ monitor và một thread khác cố gắng truy cập vào monitor đó, thread thứ hai sẽ phải chờ cho đến khi monitor trở nên khả dụng.
2. Cách thức hoạt động của việc đồng bộ hóa trong monitor
2.1. Synchronized Keyword
Trong Java, việc đồng bộ hóa thread bên trong monitor được thực hiện bằng cách sử dụng từ khóa synchronized
. Khi một thread muốn truy cập vào một phương thức hoặc một khối mã đồng bộ, nó phải “chiếm giữ” monitor của đối tượng đó.
2.1.1. Đồng bộ hóa phương thức
Khi sử dụng từ khóa synchronized
trên một phương thức, toàn bộ phương thức sẽ trở thành một khối mã đồng bộ:
public synchronized void synchronizedMethod() {
// Khối mã được đồng bộ hóa
}
2.1.2. Đồng bộ hóa khối mã
Ngoài việc đồng bộ hóa toàn bộ phương thức, bạn cũng có thể đồng bộ hóa một phần của mã bằng cách sử dụng từ khóa synchronized
trong khối mã:
public void method() {
synchronized (this) {
// Khối mã được đồng bộ hóa
}
}
2.2. Đối tượng monitor
Khi một thread thực hiện một phương thức hoặc khối mã được đồng bộ hóa, nó sẽ nắm giữ monitor của đối tượng tương ứng. Nếu một thread khác muốn vào khối mã đồng bộ hóa đó, nó sẽ bị chặn cho đến khi thread đầu tiên hoàn thành và giải phóng monitor.
2.3. Ví dụ minh họa
Dưới đây là một ví dụ về cách thức đồng bộ hóa hoạt động bên trong monitor:
class Counter {
private int count = 0;
// Phương thức đồng bộ hóa
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
// Tạo các thread
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
// Khởi động các thread
t1.start();
t2.start();
// Chờ cho đến khi các thread hoàn thành
t1.join();
t2.join();
// Xuất kết quả
System.out.println("Giá trị cuối cùng của count: " + counter.getCount());
}
}
Trong ví dụ trên:
- Chúng ta có một lớp
Counter
với phương thức increment()
được đồng bộ hóa.
- Hai thread
t1
và t2
cùng tăng giá trị của biến count
lên 1000 lần mỗi thread.
- Do
increment()
được đồng bộ hóa, chúng ta đảm bảo rằng chỉ một thread có thể thực hiện tăng giá trị của count
tại một thời điểm.
3. Các kỹ thuật đồng bộ hóa khác
3.1. Reentrant Locks
Bên cạnh việc sử dụng từ khóa synchronized
, Java cung cấp ReentrantLock
, cho phép lập trình viên kiểm soát tốt hơn việc đồng bộ hóa và thực hiện các thao tác phức tạp hơn.
3.2. Các cấu trúc đồng bộ hóa khác
Java cũng cung cấp các cấu trúc đồng bộ hóa khác như CountDownLatch
, CyclicBarrier
, và Semaphore
cho các tình huống khác nhau trong lập trình đa luồng.
4. Kết luận
Việc đồng bộ hóa thread bên trong monitor là một khía cạnh quan trọng trong lập trình đa luồng trong Java. Qua việc sử dụng từ khóa synchronized
, lập trình viên có thể đảm bảo rằng các tài nguyên chia sẻ được truy cập một cách an toàn. Tuy nhiên, cần lưu ý rằng việc đồng bộ hóa không chỉ đơn giản là sử dụng từ khóa, mà còn liên quan đến việc quản lý hiệu suất và tránh tình trạng deadlock.