Java: Khóa nội tại và đồng bộ hóa

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Đồng bộ hóa được xây dựng xung quanh một thực thể bên trong được gọi là khóa nội tại hoặc khóa màn hình. (Các đặc điểm kỹ thuật API thường đề cập đến thực thể này chỉ đơn giản là một "màn hình"). Khóa nội tại đóng một vai trò trong cả hai khía cạnh của đồng bộ hóa: thực thi truy cập độc quyền tới trạng thái của của một đối tượng và thiết lập mối quan hệ happens-before (rất cần thiết cho tầm vực).

Mỗi đối tượng có một khóa nội tại liên kết với nó. Theo quy ước, một luồng mà cần sự độc quyền và truy cập nhất quán vào các trường của một đối tượng thì phải có được khóa nội tại của đối tượng đó, và sau đó giải phóng khóa nội tại sau khi thực hiện xong. Một luồng được cho là sở hữu khóa nội tại ở khoảng thời gian từ khi nó đã có được khóa và giải phóng khóa. Chừng nào luồng còn đang sở hữu khóa nội tại thì các luồng khác không thể có được khóa tương tự. Luồng khác sẽ bị khóa khi nó cố gắng để có được khóa.

Khi một luồng giải phóng một khóa nội tại, thì một quan hệ happens-before được thiết lập giữa hành động và bất kỳ yêu cầu tiếp theo nào của khóa tương tự.

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

Khi một luồng gọi một phương thức đồng bộ hóa, nó sẽ tự động tiếp nhận khóa nội tại cho đối tượng của phương thức đó và giải phóng nó khi phương thức trả về. Việc giải phóng khóa xảy ra ngay cả khi việc trả về được gây ra bởi một ngoại lệ không bị 'bắt'.

Bạn có thể tự hỏi điều gì sẽ xảy ra khi một phương thức đồng bộ hóa tĩnh được gọi kể từ khi phương thức tĩnh được kết hợp với một lớp mà không phải với một đối tượng. Trong trường hợp này, luồng sẽ tiếp nhận khóa nội tại cho đối tượng Class kết hợp với lớp. Do đó, việc truy cập vào các trường tĩnh của lớp được điều khiển bởi một khóa khác biệt so với khóa của bất kỳ thể hiện nào của lớp.

Khối lệnh đồng bộ hóa

Một cách khác để tạo mã đồng bộ hóa là khối lệnh đồng bộ hóa. Không giống như phương thức đồng bộ hóa, khối lệnh đồng bộ hóa phải xác định đối tượng cung cấp khóa nội tại:

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

Trong ví dụ trên, phương thức addName() cần để đồng bộ hóa các thay đổi của các biến lastName và nameCount, nhưng cũng cần phải tránh các lời gọi đồng bộ hóa của các phương thức của các đối tượng khác. (Gọi các phương thức của các đối tượng khác từ mã đồng bộ hóa có thể tạo ra các vấn đề sẽ được mô tả trong phần Liveness). Nếu không có khối lệnh đồng bộ hóa, thì ta sẽ phải có một phương thức không đồng bộ hóa, riêng biệt, và nó được thể hiện thông qua lời gọi phương thức nameList.add.

Khối lệnh đồng bộ hóa cũng rất hữu ích cho việc cải thiện giữa đồng thời với đồng bộ hóa fine-grained. Giả sử, lớp MsLunch có hai thể hiện là c1 và c2, mà không bao giờ được sử dụng cùng nhau. Tất cả các cập nhật của các trường này phải được đồng bộ hóa, nhưng không có lý do gì để ngăn chặn một bản cập nhật của c1 được xen kẽ với một bản cập nhật của c2 - và điều này sẽ làm giảm đi tính đồng thời bằng cách tạo ra khối không cần thiết. Thay vì sử dụng phương thức đồng bộ hóa hoặc sử dụng khóa liên quan đến this, chúng ta tạo ra hai đối tượng duy nhất để cung cấp khóa.

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

Việc sử dụng khối lệnh đồng bộ hóa cần hết sức thận trọng. Bạn phải hoàn toàn chắc chắn rằng sẽ thực sự an toàn khi truy cập xen kẽ các trường bị ảnh hưởng.

Đồng bộ hóa reentrant

Ta cần nhớ lại rằng một luồng không thể có được khóa thuộc sở hữu của luồng khác. Nhưng một luồng có thể có được một khóa mà nó đang sở hữu. Cho phép một luồng có được khóa nhiều hơn một lần sẽ cho ta đồng bộ hóa reentrant. Điều này mô tả một tình huống trong đó mã đồng bộ hóa trực tiếp hoặc gián tiếp gọi một phương thức cũng chứa mã đồng bộ hóa, và cả hai bộ mã này cùng sử dụng một khóa. Nếu không có đồng bộ hóa reentrant thì mã đồng bộ hóa sẽ phải cần nhiều biện pháp phòng ngừa khác để tránh cho việc luồng gây ra hiệu ứng khóa chính nó.

» Tiếp: Truy cập atomic
« Trước: Phương thức đồng bộ hóa
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!