3.1 Đối tượng hoạt động
Đối tượng Hoạt động là một mẫu thiết kế phân tách chuỗi thực thi của một phương thức khỏi chuỗi mà nó được gọi. Mục đích của mẫu này là cung cấp khả năng thực thi song song bằng cách sử dụng lệnh gọi phương thức không đồng bộ và bộ lập lịch xử lý yêu cầu.
Phiên bản đơn giản hóa:
![đối tượng hoạt động](https://cdn.codegym.cc/images/article/19841270-318f-4d3e-ad78-23c7e8acf225/800.jpeg)
Biến thể cổ điển:
![Đối tượng hoạt động 2](https://cdn.codegym.cc/images/article/db9858eb-5021-40ae-a561-9989b344f1fd/800.jpeg)
Mẫu này có sáu yếu tố:
- Một đối tượng proxy cung cấp giao diện cho các phương thức công khai của máy khách.
- Một giao diện xác định các phương thức truy cập cho đối tượng đang hoạt động.
- Danh sách các yêu cầu đến từ khách hàng.
- Một bộ lập lịch xác định thứ tự thực hiện các truy vấn.
- Thực hiện các phương pháp đối tượng hoạt động.
- Một thủ tục gọi lại hoặc một biến để máy khách nhận kết quả.
khóa 3.2
Mẫu Khóa là một cơ chế đồng bộ hóa cho phép truy cập độc quyền vào tài nguyên được chia sẻ giữa nhiều luồng. Khóa là một cách để thực thi chính sách kiểm soát đồng thời.
![](https://cdn.codegym.cc/images/article/c1de466a-7e83-4372-a79e-7344cbe094f8/800.jpeg)
Về cơ bản, một khóa mềm được sử dụng, với giả định rằng mỗi luồng cố gắng “lấy khóa” trước khi truy cập tài nguyên được chia sẻ tương ứng.
Tuy nhiên, một số hệ thống cung cấp cơ chế khóa bắt buộc, theo đó nỗ lực truy cập trái phép vào tài nguyên bị khóa sẽ bị hủy bỏ bằng cách đưa ra một ngoại lệ trên chuỗi đã cố giành quyền truy cập.
Semaphore là loại khóa đơn giản nhất. Về quyền truy cập dữ liệu, không có sự phân biệt giữa các chế độ truy cập: chia sẻ (chỉ đọc) hoặc độc quyền (đọc-ghi). Ở chế độ chia sẻ, nhiều luồng có thể yêu cầu khóa để truy cập dữ liệu ở chế độ chỉ đọc. Chế độ truy cập độc quyền cũng được sử dụng trong các thuật toán cập nhật và xóa.
![mô hình khóa](https://cdn.codegym.cc/images/article/14da5562-e2ce-49f4-b330-3312f977f4b4/800.jpeg)
Các loại khóa được phân biệt bằng chiến lược chặn việc tiếp tục thực hiện luồng. Trong hầu hết các triển khai, yêu cầu khóa sẽ ngăn luồng tiếp tục thực thi cho đến khi có sẵn tài nguyên bị khóa.
Spinlock là một khóa đợi trong một vòng lặp cho đến khi quyền truy cập được cấp. Khóa như vậy rất hiệu quả nếu một luồng chờ khóa trong một khoảng thời gian ngắn, do đó tránh được việc lập lại lịch trình quá mức cho các luồng. Chi phí chờ truy cập sẽ rất lớn nếu một trong các luồng giữ khóa trong một thời gian dài.
![mẫu khóa 2](https://cdn.codegym.cc/images/article/8c7fe29c-5d7b-4888-9745-b45fbe9a0424/800.jpeg)
Để triển khai hiệu quả cơ chế khóa, cần có sự hỗ trợ ở cấp độ phần cứng. Hỗ trợ phần cứng có thể được triển khai dưới dạng một hoặc nhiều hoạt động nguyên tử, chẳng hạn như "kiểm tra và thiết lập", "tìm nạp và thêm" hoặc "so sánh và trao đổi". Các hướng dẫn như vậy cho phép bạn kiểm tra mà không bị gián đoạn rằng khóa còn trống hay không và nếu có thì hãy lấy khóa.
3.3 Màn hình
Mẫu Giám sát là một cơ chế đồng bộ hóa và tương tác quy trình cấp cao cung cấp quyền truy cập vào các tài nguyên được chia sẻ. Một cách tiếp cận để đồng bộ hóa hai hoặc nhiều tác vụ máy tính bằng cách sử dụng một tài nguyên chung, thường là phần cứng hoặc một tập hợp các biến.
Trong đa nhiệm dựa trên màn hình, trình biên dịch hoặc trình thông dịch chèn mã khóa-mở khóa vào các quy trình được định dạng thích hợp một cách minh bạch, rõ ràng đối với lập trình viên, giúp lập trình viên không phải gọi các nguyên hàm đồng bộ hóa một cách rõ ràng.
![](https://cdn.codegym.cc/images/article/3b7a2de5-610e-43da-9752-9ae8fa604eb8/800.jpeg)
Màn hình bao gồm:
- một tập hợp các thủ tục tương tác với một tài nguyên được chia sẻ
- câm
- các biến được liên kết với tài nguyên này
- một bất biến xác định các điều kiện để tránh một điều kiện cuộc đua
Thủ tục giám sát thu được mutex trước khi bắt đầu công việc và giữ nó cho đến khi thủ tục thoát hoặc cho đến khi một điều kiện nhất định được chờ đợi. Nếu mỗi thủ tục đảm bảo rằng bất biến là đúng trước khi giải phóng mutex, thì không tác vụ nào có thể lấy được tài nguyên trong điều kiện chạy đua.
Đây là cách toán tử đồng bộ hóa hoạt động trong Java với các phương thức wait()
và notify()
.
3.4 Khóa kiểm tra kép
Khóa được kiểm tra kép là một mẫu thiết kế song song nhằm giảm chi phí lấy khóa.
Đầu tiên, điều kiện chặn được kiểm tra mà không có bất kỳ đồng bộ hóa nào. Một luồng cố gắng lấy khóa chỉ khi kết quả kiểm tra chỉ ra rằng nó cần lấy khóa.
//Double-Checked Locking
public final class Singleton {
private static Singleton instance; //Don't forget volatile modifier
public static Singleton getInstance() {
if (instance == null) { //Read
synchronized (Singleton.class) { //
if (instance == null) { //Read Write
instance = new Singleton(); //
}
}
}
}
Làm cách nào để tạo một đối tượng đơn lẻ trong môi trường an toàn theo luồng?
public static Singleton getInstance() {
if (instance == null)
instance = new Singleton();
}
Nếu bạn tạo một đối tượng Singleton từ các luồng khác nhau, thì có thể xảy ra trường hợp một số đối tượng được tạo cùng một lúc và điều này là không thể chấp nhận được. Do đó, thật hợp lý khi bao bọc việc tạo đối tượng trong một câu lệnh được đồng bộ hóa.
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
Cách tiếp cận này sẽ hiệu quả, nhưng nó có một nhược điểm nhỏ. Sau khi đối tượng được tạo, mỗi khi bạn cố gắng lấy nó trong tương lai, một kiểm tra sẽ được thực hiện trong khối được đồng bộ hóa, điều đó có nghĩa là luồng hiện tại và mọi thứ được kết nối với nó sẽ bị khóa. Vì vậy, mã này có thể được tối ưu hóa một chút:
public static Singleton getInstance() {
if (instance != null)
return instance;
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
Trên một số ngôn ngữ và/hoặc trên một số máy, không thể triển khai mẫu này một cách an toàn. Do đó, đôi khi nó được gọi là một anti-pattern. Các tính năng như vậy đã dẫn đến sự xuất hiện của mối quan hệ trật tự nghiêm ngặt "xảy ra trước" trong Mô hình bộ nhớ Java và Mô hình bộ nhớ C++.
Nó thường được sử dụng để giảm chi phí triển khai khởi tạo chậm trong các chương trình đa luồng, chẳng hạn như mẫu thiết kế Singleton. Trong quá trình khởi tạo chậm một biến, quá trình khởi tạo được hoãn lại cho đến khi giá trị của biến được cần trong tính toán.
3.5 Bộ lập lịch
Bộ lập lịch là một mẫu thiết kế song song cung cấp một cơ chế để thực hiện chính sách lập lịch, nhưng độc lập với bất kỳ chính sách cụ thể nào. Kiểm soát thứ tự các luồng sẽ thực thi mã tuần tự, sử dụng một đối tượng chỉ định rõ ràng chuỗi các luồng đang chờ.
![](https://cdn.codegym.cc/images/article/6cf63a65-1fe5-44f8-a43c-6f5219728c9a/800.jpeg)