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.
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.
Monitor có hai tính chất chính:
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 đó.
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 }
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 } }
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.
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:
Counter
với phương thức increment()
được đồng bộ hóa.t1
và t2
cùng tăng giá trị của biến count
lên 1000 lần mỗi thread.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.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.
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.
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.