Sự khác biệt giữa khóa trong một Collections.synchronizedMap()
và một ConcurrentHashMap
là như sau:
Nếu có nhiều chủ đề sẽ truy cập vào một Collections.synchronizedMap()
thường xuyên, sẽ có rất nhiều tranh cãi vì mỗi phương pháp được đồng bộ bằng một khóa chia sẻ (ví dụ: nếu thread X gọi phương thức trên Collections.synchronizedMap()
, tất cả các chủ đề khác sẽ bị chặn gọi bất kỳ phương thức nào trên Collections.synchronizedMap()
cho đến khi chuỗi X trả về từ phương thức được gọi).
A ConcurrentHashMap
có số lượng khóa thay đổi (mặc định là 16) mà mỗi khóa bảo vệ một đoạn khóa trong ConcurrentHashMap
. Vì vậy, đối với một ConcurrentHashMap
với 160 phím, mỗi khóa sẽ bảo vệ 10 phần tử. Do đó, các phương thức hoạt động trên một khóa (get
, put
, set
, v.v.) chỉ khóa quyền truy cập vào các phương thức khác hoạt động trên khóa mà các phím nằm trong cùng một phân đoạn. Ví dụ: nếu chuỗi X gọi put(0, someObject)
và sau đó, chuỗi Y gọi put(10, someOtherObject)
các cuộc gọi đó có thể thực thi đồng thời và chuỗi Y không phải chờ chuỗi X trở về từ put(0, someObject)
. Một ví dụ được cung cấp dưới đây.
Ngoài ra, một số phương pháp nhất định như size()
và isEmpty()
không được bảo vệ. Mặc dù điều này cho phép đồng thời nhiều hơn, điều đó có nghĩa là chúng không nhất quán (chúng sẽ không phản ánh trạng thái đồng thời thay đổi).
public static void main(String[] args) {
ConcurrentHashMap<Integer, Object> map = new ConcurrentHashMap<>(160);
new Thread(new Runnable() {
@Override
public void run() {
map.put(0, "guarded by one lock");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
map.put(10, "guarded by another lock");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
// could print 0, 1, or 2
System.out.println(map.count());
}
}.start();
}
Nguồn
2013-04-22 16:22:58
OK, tôi hiểu. Nhưng còn nếu hai hoặc nhiều luồng đang cố sửa đổi tất cả trong mảng phụ {0,63} thì sao? – GedankenNebel
Sau đó, nó đến trước được phục vụ trước tiên - luồng đầu tiên để lấy khóa làm thay đổi của nó, sau đó khi nó kết thúc luồng thứ hai làm thay đổi. 'ConcurrentHashMap' có các phương thức như' replace' để đảm bảo rằng luồng thứ hai không vô tình ghi đè lên các thay đổi của luồng đầu tiên. –
Tôi không nghĩ rằng nó thực sự là "đầu tiên đến trước được phục vụ", như tôi đã hiểu (tôi không có báo giá chính xác, nhưng tôi đã học được từ Java Concurrency in Practice), sự công bằng chỉ được đảm bảo khi nó rõ ràng, như trong các nhà xây dựng để triển khai 'Lock' khác nhau rõ ràng, chẳng hạn như' ReentrantLock' hoặc Hàng đợi như 'ArrayBlockingQueue'. (Tôi biết đó là một chủ đề cũ, xin lỗi) – Marcelo