Design Patterns – Object Pool
Người viết: Lê Tất Tùng 1. Intent Khi cần phải làm việc với một số lượng lớn các đối tượng “tốn kém” để nhanh chóng và mỗi đối tượng chỉ cần một thời gian ngắn thì hiệu suất của toàn bộ ứng dụng có thể bị ảnh hưởng.Trong trường hợp này, chúng ta nên sử dụng mô hình ...
Người viết: Lê Tất Tùng
1. Intent
Khi cần phải làm việc với một số lượng lớn các đối tượng “tốn kém” để nhanh chóng và mỗi đối tượng chỉ cần một thời gian ngắn thì hiệu suất của toàn bộ ứng dụng có thể bị ảnh hưởng.Trong trường hợp này, chúng ta nên sử dụng mô hình Object Pool. Object Pool được sử dụng để quản lý bộ nhớ đệm lưu trữ các đối tượng. Một client có quyền truy cập vào Object pool thay vì tạo ra một đối tượng mới thì chỉ cần đơn giản yêu cầu các Object pool cho một đối tượng đã có sẵn trong object pool để thay thế . Object pool thông thường hoạt động theo kiểu : tự tạo đối tượng mới nếu mình chưa có sẵn hoặc chúng ta có thể tự tạo 1 object pool chứa hạn chế đối tượng trong đó.
2. Example
Cơ chế hoạt động của Object pool tương tự như một kho văn phòng. Khi một nhân viên mới được tuyển dụng, quản lý văn phòng phải chuẩn bị một không gian làm việc cho anh ta. Nếu các thiết bị phụ tùng đã có sẵn trong kho, quản lý sẽ đến kho và lấy các thiết bị đó. Nếu không, quản lý sẽ phải đặt mua các thiết bị mới. Trong trường hợp nếu một nhân viên bị sa thải, thiết bị của anh ta được chuyển tới nhà kho, các thiết bị đó có thể sử dụng cho một nhân viên mới nào đó sau này.
3. Discussion
3.1 Problem
Nhìn từ ví dụ thực tế trên, chúng ta có thể thấy ngay vấn đề của object pool : Thứ nhất, sử dụng object pool đồng nghĩa với việc chúng ta phải tốn thêm tài nguyên cho đối tượng object pool. Quá rõ ràng, muốn lưu trữ thiết bị thì phải có nhà kho (trong thực tế công ty lại tốn chi phí, diện tích, nhân viên quản lý nhà kho). Thứ hai, nếu đồ trong kho quá cũ đến một thời điểm nào đó sẽ không sử dụng được. Ví dụ cần 1 Iphone 7s cho nhân viên mới, trong khi đó trong kho chỉ có Nokia 1080 …(RIP). Trong trường hợp này, chúng ta vừa tốn tài nguyên mà lại không thể sử dụng được tài nguyên đó.
3.2 Solution
Vấn đề thứ nhất: Đó là tất yếu. Với dung lượng khủng của các máy tính ngày nay, tôi nghĩ vấn đề này sẽ không ảnh hưởng nhiều đến các chương trình.
Vấn đề thứ 2: Object pool cho phép các đối tượng khác “check out ” các đối tượng chứa trong nó, khi các đối tượng không còn cần thiết bởi các quá trình của chương trình, chúng được sẽ được giải phóng khỏi object pool. Tuy nhiên, chúng ta không muốn một quá trình phải đợi cho tới khi một đối tượng cụ thể được giải phóng, vì vậy Object Pool cũng tạo ra các đối tượng mới khi chúng được yêu cầu nhưng cũng phải thực hiện làm sạch các đối tượng không sử dụng theo định kỳ.
4. Structure
Ý tưởng chung cho mô hình Connection Pool là nếu các instances của một lớp có thể được tái sử dụng, thay vì khởi tạo một instances mới khi cần, bạn có thể tái sử dụng chúng.
5. Participants
- Reusable – Các đối tượng có thể tái sử dụng.
- Client – Các lớp có vai trò sử dụng các đối tượng có thể tái sử dụng được.
- ReusablePool – Các lớp có vai trò quản lý các đối tượng có thể tái sử dụng để cung cấp cho các đối tượng Client.
6. Applicability
Sử dụng mẫu Object Pool khi:
- Các đối tượng được tạo ra một cách khá tốn kém. Ví dụ: truy vấn database … (phân bổ chi phí)
- Bạn cần tạo một số lượng lớn các đối tượng trong thời gian ngắn (phân mảnh bộ nhớ)
7. Handling of empty pools
Đối tượng hồ chứa sử dụng một trong ba chiến lược để xử lý một yêu cầu từ client khi trong object pool không chứa đối tượng nào (rỗng).
- Không cung cấp một đối tượng (và trả lại lỗi cho client).
- Nạp thêm một đối tượng mới.
- Trong một môi trường đa luồng, một object pool có thể chặn các yêu cầu từ client cho đến khi một luồng khác trả về một đối tượng có thể sử dụng vào object pool.
8. Pitfalls
Khi triển khai mô hình Object pool, lập trình viên phải cẩn thận để đảm bảo rằng trạng thái của các đối tượng quay trở lại object pool phải được đặt ở trạng thái hợp lý cho việc sử dụng tiếp theo của đối tượng. Nếu không kiểm soát được điều này, đối tượng sẽ thường ở trong một số trạng thái mà chương trình client không mong đợi và có thể làm cho chương trình client thất bại. Việc thiết lập lại các đối tượng không thích hợp cũng có thể gây rò rỉ thông tin. Nếu đối tượng chứa dữ liệu bí mật (ví dụ: số thẻ tín dụng của người dùng) không bị xóa trước khi đối tượng được chuyển tới một máy khách mới thì khách hàng độc hại hoặc lỗi có thể tiết lộ dữ liệu cho bên không được ủy quyền.
9. Code examples
(Copyright (c) 2014-2016 Ilkka Seppälä )
Step1: Tạo 1 class Oliphaunt (để tạo ra Oliphaunt khá “tốn kém”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public class Oliphaunt { private static int counter = 1; private final int id; /** * Constructor */ public Oliphaunt() { id = counter++; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } public int getId() { return id; } @Override public String toString() { return String.format("Oliphaunt id=%d", id); } } |
Step2 : Tạo ra 1 đối tượng tổng quát với kiểu là T – ObjectPool
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import java.util.HashSet; import java.util.Set; public abstract class ObjectPool<T> { private Set<T> available = new HashSet<>(); private Set<T> inUse = new HashSet<>(); protected abstract T create(); /** * Checkout object from pool */ public synchronized T checkOut() { if (available.isEmpty()) { available.add(create()); } T instance = available.iterator().next(); available.remove(instance); inUse.add(instance); return instance; } public synchronized void checkIn(T instance) { inUse.remove(instance); available.add(instance); } @Override public String toString() { return String.format("Pool available=%d inUse=%d", available.size(), inUse.size()); } } |
Step 3: Tạo object pool kế thừa từ ObjectPool với kiểu T là Oliphaunt
1 2 3 4 5 6 7 8 9 |
public class OliphauntPool extends ObjectPool<Oliphaunt> { @Override protected Oliphaunt create() { return new Oliphaunt(); } } |
Step 4 : Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class App { private static final Logger LOGGER = LoggerFactory.getLogger(App.class); public static void main(String[] args) { OliphauntPool pool = new OliphauntPool(); LOGGER.info(pool.toString()); Oliphaunt oliphaunt = pool.checkOut(); LOGGER.info("Checked out {}", oliphaunt); LOGGER.info(pool.toString()); } } |
10. References
https://en.wikipedia.org/wiki/Object_pool_patternhttps://sourcemaking.com/design_patterns/object_pool https://github.com/iluwatar/java-design-patterns/tree/master/object-pool
Techtalk via Viblo