Java Concurrent Hashmap initTable () Tại sao lại chặn thử / cuối cùng?
Tôi đã xem mã sau (lấy từ đây ) '
/**
* Initializes table, using the size recorded in sizeCtl.
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
Ai đó có thể giải thích lý do tại sao cần có khối thử?
Trả lời
các ngoại lệ và lỗi không được kiểm tra tồn tại như một khái niệm. Nếu bạn gọi .put()một Bản đồ ConcurrentHashMap và bạn thực sự thiếu bộ nhớ, bản đồ đó có thể cố gắng tạo một mảng và lệnh gọi đó có thể không thành công với Lỗi OutOfMemoryError. Mã sẽ vẫn tiếp tục (có thể là tại khối bắt được điều này), và các tham chiếu đến bản đồ đó vẫn tồn tại. Sẽ hơi tệ nếu sau đó, bản đồ bị lỗi và hoàn toàn mất hiệu lực vì sizeCtl có giá trị bị hỏng.
Vấn đề là khối cuối cùng, khôi phục sizeCtlgiá trị. Điều này được sử dụng cho nhiều thứ khác nhau, đáng chú ý bao gồm quản lý luồng nào có quyền truy cập. Nếu không, bất kỳ lệnh gọi nào khác sẽ quay mãi mãi.
Mức độ liên quan của điều này (chẳng hạn như tần suất có thể ném xảy ra ở đây, so với việc loại bỏ 'thử' và 'cuối cùng' và chỉ kết thúc bằng sizeCtl = sc;mà không có thêm chi phí thử / cuối cùng) là thấp, nhưng nếu nó có liên quan, nó khá phù hợp.
Đó là một loại mutex - và một khóa kiểm tra hai lần.
Đầu tiên
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
kiểm tra sizeCtlmutex. Nếu nó nhỏ hơn 0, có người khác đã nắm lấy nó, vì vậy chúng ta chỉ cần chờ đợi một chút.
Nếu nó bằng 0 (hoặc nhiều hơn), rất có thể chúng ta sẽ lấy được khóa. Vì vậy, So sánh và Hoán đổi được thực hiện:
if (U.compareAndSetInt(this, SIZECTL, sc, -1)
Đây là một hoạt động nguyên tử - nếu một luồng khác nắm lấy nó giữa lần kiểm tra thứ nhất và thứ hai, thì ifsẽ không được thực hiện.
Nếu CAS thành công, thì phương pháp này có trách nhiệm giải phóng mutex. Do đó, a try-finallyđược sử dụng để đảm bảo rằng mutex được giải phóng ( sizeCtlđặt lại về giá trị ban đầu) bất kể có ngoại lệ xảy ra hay không (ví dụ: hết bộ nhớ gán Nút mới - hoặc nếu có một hạn chế duy nhất áp dụng cho bản đồ) hay không.