×

Sử dụng synchronized để bảo vệ tài nguyên chia sẻ trong Java

Khi phát triển các ứng dụng đa luồng trong Java, vấn đề đồng bộ hóa (synchronization) là yếu tố quan trọng để đảm bảo rằng các tài nguyên được chia sẻ giữa các luồng được bảo vệ và không bị truy cập cùng lúc gây ra lỗi. Một trong những công cụ mạnh mẽ và phổ biến nhất để xử lý vấn đề này là từ khóa synchronized.

Khái niệm về đồng bộ hóa

Đồng bộ hóa trong Java là cơ chế giúp điều chỉnh quyền truy cập đến các tài nguyên chia sẻ một cách an toàn. Bằng việc sử dụng các câu lệnh đồng bộ hóa, bạn có thể đảm bảo rằng chỉ có một luồng duy nhất có thể thực hiện hành động nào đó với tài nguyên tại một thời điểm.

Từ khóa synchronized

Từ khóa synchronized trong Java có thể được sử dụng theo hai cách chính: đồng bộ hóa phương thức (synchronized method) và đồng bộ hóa khối mã (synchronized block).

Đồng bộ hóa phương thức

Phương thức đồng bộ hóa sẽ khoá đối tượng hiện tại (trong trường hợp là phương thức không tĩnh) hoặc khoá lớp (trong trường hợp là phương thức tĩnh). Dưới đây là ví dụ về việc sử dụng synchronized cho một phương thức:

public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

Trong ví dụ này, phương thức incrementgetCount được đồng bộ hóa. Điều này có nghĩa là, khi một luồng đang thực hiện tăng giá trị của count, không có luồng nào khác có thể truy cập các phương thức này của đối tượng đó.

Đồng bộ hóa khối mã

Đồng bộ hóa khối mã linh hoạt hơn và cho phép bạn giới hạn phạm vi của việc đồng bộ hóa. Bạn có thể đồng bộ hóa bất kỳ khối mã nào trong một phương thức bằng cách sử dụng từ khóa synchronized:

public class Counter {
    private int count = 0;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }

    public int getCount() {
        synchronized (this) {
            return count;
        }
    }
}

Trong ví dụ trên, chỉ một khối mã nhỏ được đồng bộ hóa, điều này có thể cải thiện hiệu suất nếu chỉ có một phần của phương thức cần được đồng bộ hóa.

Sử dụng đối tượng khác làm khóa

Nếu cần thiết, bạn cũng có thể sử dụng bất kỳ đối tượng nào khác làm khóa trong khối đồng bộ hóa:

public class Counter {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    public int getCount() {
        synchronized (lock) {
            return count;
        }
    }
}

Trong ví dụ này, chúng ta sử dụng đối tượng lock để đồng bộ hóa thay vì sử dụng this. Điều này giúp tăng cường kiểm soát đối với việc đồng bộ hóa, đặc biệt hữu ích trong các tình huống phức tạp.

Lợi ích và hạn chế của đồng bộ hóa

Lợi ích

  1. An toàn dữ liệu: Đảm bảo rằng dữ liệu không bị thay đổi bất ngờ và không nhất quán khi có nhiều luồng truy cập.
  2. Dễ hiểu, dễ sử dụng: synchronized là từ khóa được tích hợp sẵn và dễ dàng sử dụng mà không cần thêm các thư viện bên ngoài.

Hạn chế

  1. Hiệu suất: Đồng bộ hóa có thể làm giảm hiệu suất của ứng dụng do các luồng phải chờ đợi nhau.
  2. Deadlock: Khi sử dụng không cẩn thận, việc đồng bộ hóa có thể dẫn đến deadlock, làm cho ứng dụng bị treo.

Kết luận

Sử dụng synchronized là một cách tiếp cận đơn giản và hiệu quả để bảo vệ các tài nguyên chia sẻ trong lập trình đa luồng. Tuy nhiên, việc hiểu rõ và sử dụng hợp lý các cơ chế đồng bộ hóa là cần thiết để tránh các vấn đề về hiệu suất và độ phức tạp trong quản lý luồng.

Comments