Có một Danh sách đồng thời trong JDK của Java không?


277

Làm cách nào tôi có thể tạo một thể hiện Danh sách đồng thời, nơi tôi có thể truy cập các phần tử theo chỉ mục? JDK có bất kỳ lớp hoặc phương thức xuất xưởng nào tôi có thể sử dụng không?


28
Tại sao không mang tính xây dựng? Một số CopyOnWriteArrayList được đề xuất không tìm thấy trong .Net. Bạn có thể nói rằng cả hai câu hỏi liên quan đến nhau nhưng không đóng câu hỏi này !!!
AlikElzin-kilaka

1
Tôi không biết tại sao Jarrod Roberson lại nghĩ rằng đó là một ý tưởng tốt để thực hiện các chỉnh sửa chi tiết được thực hiện bởi Stephan và hoàn nguyên chúng trở lại câu hỏi ban đầu, không đúng ngữ pháp. Câu trả lời của Jarrod vẫn là một câu trả lời hoàn toàn chấp nhận được. Trong thực tế, CopyOnWriteArrayList là Danh sách triển khai đồng thời duy nhất trong JDK. Bối rối ...
Matt Passell 20/07/2015

10
Bởi vì câu trả lời được chấp nhận là cho câu hỏi ban đầu và Stephan đã đặt một câu hỏi hoàn toàn không liên quan với một loạt mã nguồn mà người đăng ban đầu không bao gồm bất kỳ nơi nào thay đổi hoàn toàn câu hỏi, điều này tạo ra nhiều câu trả lời gợi ý những điều khác ngoài câu hỏi Listban đầu nói là một yêu cầu được coi là phá hoại. Một người điều hành đã khóa câu hỏi vì những người phàn nàn rằng các câu trả lời không trả lời phiên bản phá hoại của câu hỏi.

1
/ locked/ closed/ nhận xét trước

8
Không có lý do cho câu hỏi này được đóng lại. Nó hỏi về các lớp trong JDK, không có gì giống như tìm kiếm một thư viện; nó là nền tảng của Java.
maaartinus

Câu trả lời:


175

Có một triển khai danh sách đồng thời trong java.util.conc hiện . CopyOnWriteArrayList nói riêng.


84
Lưu ý rằng nó sao chép toàn bộ danh sách trên mỗi lần chèn, vì vậy nó thường không hiệu quả.
dfrankow

22
@dfrankow Nhưng nó có thể nhiều hơn hiệu quả nếu bạn đang lặp lại nhiều hơn bạn đang cập nhật.
b1nary.atr0phy

Không hoạt động tốt như hiển thị ở đây. Tôi có ngoại lệ mặc dù tôi chỉ sử dụng phương thức addAll của nó và đọc nó bằng luồng. stackoverflow.com/questions/1527519/ từ
devssh 26/03/18

166

Nếu bạn không quan tâm đến việc có quyền truy cập dựa trên chỉ mục và chỉ muốn các đặc điểm duy trì thứ tự chèn của Danh sách, bạn có thể xem xét java.util.concản.ConcienLinkedQueue . Vì nó thực hiện Iterable, nên khi bạn đã thêm xong tất cả các mục, bạn có thể lặp lại nội dung bằng cách sử dụng cú pháp nâng cao:

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}

4
Tôi nghĩ rằng đơn giản hóa cho câu lệnh ( :) được gọi là foreach: docs.oracle.com/javase/1.5.0/docs/guide/lingu/foreach.html
AlikElzin-kilaka

2
@ AlikElzin-kilaka Bạn nói đúng. Tôi nghĩ rằng tên đó luôn làm phiền tôi, bởi vì cú pháp thực tế không bao gồm từ "mỗi", nhưng tôi sẽ cập nhật câu trả lời để sử dụng tên chính thức. :)
Matt Passell

3
@ AlikElzin-kilaka Nitpicking, nhưng theo phiên bản 8 của JLS, nó được gọi là "tuyên bố nâng cao". Điều tương tự trong hướng dẫn java .
Roland

1
@Roland chắc chắn KHÔNG nitpicking. (Hiện tại) có một sự khác biệt giữa "cho mỗi" và "được tăng cường cho" trong Java.
hfontanez

2
@Roland gián tiếp. Tôi tin rằng họ đã đổi tên "cho mỗi vòng lặp" thành "được tăng cường" để loại bỏ sự nhầm lẫn giữa Stream.forEach và ngày nay được gọi là nâng cao cho.
hfontanez

128

Bạn rất có thể sử dụng Collections.syn syncizedList (List) nếu tất cả những gì bạn cần là đồng bộ hóa lời gọi đơn giản:

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

70
Kết quả synchronizedListlà "đồng bộ hóa" nhưng không "đồng thời". Một vấn đề cơ bản mà nhiều hoạt động Danh sách - dựa trên chỉ mục - bản thân chúng không phải là nguyên tử và cần phải là một phần của cấu trúc loại trừ lẫn nhau lớn hơn.

6
IMO, asing a Vectorlà đơn giản hơn là Collections.synchronizedList(new ArrayList<Object>()).
Stephan

8
Kết quả của syncList là "được đồng bộ hóa" nhưng không "đồng thời".
Kanagavelu Sugumar

46

Bởi vì hành động giành được vị trí và lấy phần tử từ vị trí đã cho tự nhiên đòi hỏi một số khóa (bạn không thể có danh sách có thay đổi cấu trúc giữa hai thao tác đó).

Ý tưởng của bộ sưu tập đồng thời là mỗi thao tác tự nó là nguyên tử và có thể được thực hiện mà không cần khóa / đồng bộ hóa rõ ràng.

Do đó, việc đưa phần tử vào vị trí ntừ một Listhoạt động nguyên tử nhất định sẽ không có ý nghĩa quá nhiều trong tình huống có thể truy cập đồng thời.


6
Joachim, tôi nghĩ bạn đánh vào đầu đinh. Lấy ví dụ, một danh sách chỉ đọc như một danh sách đồng thời. Lấy phần tử ở vị trí N từ danh sách không chỉ có ý nghĩa, mà nó là tóm tắt của vấn đề. Vì vậy, một danh sách bất biến (chữ thường L) sẽ là một ví dụ tốt, nhưng nó không phải là Danh sách (chữ L viết hoa). CopyOnWriteArrayList đồng thời, nhưng nhiều người không thích hiệu suất. Một giải pháp dọc theo các sợi dây (dây thừng) có lẽ sẽ là một người chiến thắng tốt.
johnstosh

1
Điểm rất tốt. Nhưng Danh sách mà OP đang sử dụng có thể có cách sử dụng rất cụ thể. Ví dụ, nó có thể được điền vào môi trường đồng thời, sau đó "khóa" (bất kể nó có nghĩa là gì) và sau đó được truy cập an toàn bởi chỉ mục. Vì vậy, trong giai đoạn đầu tiên điền vào Danh sách như vậy vẫn sẽ yêu cầu triển khai an toàn luồng. Thật không may, OP đã không cụ thể về cách Danh sách mà anh ấy đang tìm kiếm sẽ được sử dụng.
igor.zh

13

Bạn có các tùy chọn sau:

  • Collections.synchronizedList(): Bạn có thể quấn bất kỳ Listthực hiện ( ArrayList, LinkedListhoặc một danh sách của bên thứ 3). Truy cập vào mọi phương thức (đọc và viết) sẽ được bảo vệ bằng cách sử dụng synchronized. Khi sử dụng iterator()hoặc nâng cao cho vòng lặp, bạn phải đồng bộ hóa thủ công; trong khi lặp lại, các luồng khác bị chặn hoàn toàn ngay cả khi đọc. Bạn cũng có thể đồng bộ hóa riêng cho từng cuộc gọi hasNextnextsau đó ConcurrentModificationExceptionlà có thể.

  • CopyOnWriteArrayList: tốn kém để sửa đổi, nhưng chờ đợi để đọc. Trình vòng lặp không bao giờ ném ConcurrentModificationException, chúng trả về một ảnh chụp nhanh của danh sách tại thời điểm tạo vòng lặp, ngay cả khi danh sách được sửa đổi bởi một luồng khác trong khi lặp. Hữu ích cho danh sách cập nhật không thường xuyên. Các hoạt động hàng loạt như addAllđược ưu tiên để cập nhật - mảng bên trong được sao chép ít lần hơn.

  • Vector: rất thích synchronizedList, nhưng lặp lại cũng được đồng bộ hóa. Tuy nhiên, các trình vòng lặp có thể ném ConcurrentModificationException, nếu vectơ được sửa đổi bởi một luồng khác trong khi lặp.

Sự lựa chọn khác:

  • Collections.unmodifiableList(): không khóa, an toàn cho chuỗi, nhưng không thể sửa đổi
  • Queuehoặc Dequecó thể là một sự thay thế nếu bạn chỉ thêm / xóa ở cuối danh sách và lặp lại danh sách. Không có quyền truy cập theo chỉ mục và không thêm / xóa tại các vị trí tùy ý. Họ có nhiều triển khai đồng thời với hiệu suất tốt hơn và truy cập đồng thời tốt hơn, nhưng nó vượt quá phạm vi của câu hỏi này. Bạn cũng có thể xem JCTools , chúng chứa nhiều triển khai hàng đợi hiệu suất hơn dành riêng cho người tiêu dùng hoặc nhà sản xuất duy nhất.

4

nhập mô tả hình ảnh ở đây

CopyOnWriteArrayList là một biến thể của ArrayList an toàn theo luồng trong đó tất cả các hoạt động đột biến (thêm, đặt, v.v.) được thực hiện bằng cách tạo một bản sao mới của mảng bên dưới.

CopyOnWriteArrayList là một thay thế đồng thời của giao diện Danh sách được đồng bộ hóa và một phần của gói java.util.conc hiện tại là một bộ sưu tập an toàn luồng.

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList không an toàn và không ném ConcurrencyModificationException khi bên dưới CopyOnWriteArrayList được sửa đổi trong Iteration sử dụng một bản sao riêng của ArrayList.

Điều này thường quá tốn kém vì mảng sao chép liên quan đến mọi hoạt động cập nhật, một bản sao được sao chép sẽ được tạo. CopyOnWriteArrayList là lựa chọn tốt nhất chỉ cho hoạt động đọc thường xuyên.

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.