2013-04-03 20 views
7

Vì c đã được đồng bộ hóa bộ sưu tập và do đó chỉ an toàn. Nhưng tại sao chúng ta phải sử dụng lại synchronized(c) cho lần lặp lại? Thực sự bối rối. Cảm ơn.Bộ sưu tập được đồng bộ hóa

"Điều bắt buộc là người sử dụng bằng tay đồng bộ hóa trên các trở bộ sưu tập khi iterating trên nó:

Collection c = Collections.synchronizedCollection(myCollection); 
... 
synchronized(c) { 
    Iterator i = c.iterator(); // Must be in the synchronized block 
    while (i.hasNext()) { 
     foo(i.next()); 
    } 
} 

Thất bại trong việc làm theo lời khuyên này có thể dẫn đến hành vi không xác định." http://docs.oracle.com/javase/6/docs/api/java/util/Collections.

Trả lời

10

Việc triển khai bộ sưu tập synchronized nhất có thể làm là đảm bảo rằng mỗi cuộc gọi phương thức cá nhân riêng lẻ được đồng bộ hóa. Nhưng lặp lại nhất thiết phải liên quan đến nhiều cuộc gọi phương thức riêng biệt, vì vậy bạn, người dùng của bộ sưu tập được đồng bộ hóa, phải đồng bộ hóa trên toàn bộ quá trình lặp lại.

Ví dụ, nếu bạn không đồng bộ hóa trên c, các nội dung của bộ sưu tập có thể thay đổi giữa i.hasNext()i.next() - nó thậm chí có thể đi từ việc có yếu tố để không có nhiều yếu tố, trong trường hợp này i.next() sẽ thất bại.

+0

Bạn có ý nghĩa gì bởi "nhất thiết phải liên quan đến nhiều cuộc gọi phương thức riêng biệt"? – user697911

+1

@ user697911: 'i.hasNext()' và 'i.next()' là hai cuộc gọi phương thức riêng biệt và không có cách nào để thực hiện 'Collections.synchronizedCollection' để đảm bảo rằng không có gì thay đổi ở giữa' i.hasNext() 'và' i.next() '. –

+0

Vì vậy, concurrentHashmap sẽ giống nhau? Chúng ta cũng phải đồng bộ hóa concurrenthashamp trong lần lặp lại không? – user697911

4

Làm cho tất cả các phương thức trên một lớp được đồng bộ hóa riêng lẻ sẽ không tạo một tập hợp (gọi chúng trong một nhóm) của các luồng phương thức đó an toàn. Bằng cách gói Iterator trong một khối đồng bộ, bạn đang bảo vệ cá thể cụ thể của trình lặp đó khỏi phương thức riêng của nó được gọi là xen kẽ với các cuộc gọi khác bởi nhiều luồng.

Nếu tôi gọi .add() khi nó là an toàn, nếu tôi cần phải gọi .add() nhiều lần để hoàn thành một tuyên bố hợp lý, không có đảm bảo rằng ai đó đã không được thêm cái gì khác hoặc gỡ bỏ cái gì khác giữa .add() cuộc gọi của tôi, trừ khi tôi chặn mọi thứ khác khỏi gọi .add() (hoặc bất kỳ phương pháp nào khác) bằng cách synchronizing trên biến đại diện cho bộ sưu tập.

Iterator làm cho mutiple gọi đến các phương thức riêng lẻ trên bộ sưu tập, tất cả chúng phải được bao bọc trong một khối synchronized để làm cho chúng hoạt động như một đơn transaction các loại. Kiểm tra mã nguồn của việc thực hiện của Iterator bạn sẽ thấy những gì tôi có ý nghĩa. Đây là mã nguồn cho List nó làm cho nhiều cuộc gọi riêng lẻ đến việc triển khai cơ bản, vì vậy tất cả chúng cần phải được thực hiện theo thứ tự không bị gián đoạn bởi cùng một luồng để xác định.

@Override 
    public Iterator<A> iterator() { 
     if (tail == null) 
      return emptyIterator(); 
    return new Iterator<A>() { 
     List<A> elems = List.this; 
     public boolean hasNext() { 
     return elems.tail != null; 
     } 
     public A next() { 
       if (elems.tail == null) 
        throw new NoSuchElementException(); 
     A result = elems.head; 
     elems = elems.tail; 
     return result; 
     } 
     public void remove() { 
     throw new UnsupportedOperationException(); 
     } 
    }; 
    } 

Các source cho AbstractList.iterator() cho thấy thậm chí logic phức tạp hơn mà làm cho nhiều cuộc gọi.

Trình bao bọc tốt hơn là gói chúng trong Immutable bộ sưu tập, sau đó bạn đảm bảo rằng không có gì khác có thể thay đổi bộ sưu tập cơ bản giữa các cuộc gọi.

+1

giải thích tốt nhất tôi đã gặp! – kellogs