Tôi nên sử dụng triển khai Hàng đợi đồng thời nào trong Java?


132

Từ JavaDocs:

  • Một concienLinkedQueue là một lựa chọn thích hợp khi nhiều luồng sẽ chia sẻ quyền truy cập vào một bộ sưu tập chung. Hàng đợi này không cho phép các phần tử null.
  • ArrayBlockingQueue là một "bộ đệm giới hạn" cổ điển, trong đó một mảng có kích thước cố định chứa các phần tử được chèn bởi các nhà sản xuất và được trích xuất bởi người tiêu dùng. Lớp này hỗ trợ một chính sách công bằng tùy chọn để đặt hàng các chủ đề của nhà sản xuất và người tiêu dùng đang chờ
  • LinkedBlockingQueue thường có thông lượng cao hơn hàng đợi dựa trên mảng nhưng hiệu suất dự đoán ít hơn trong hầu hết các ứng dụng đồng thời.

Tôi có 2 kịch bản, một kịch bản yêu cầu hàng đợi để hỗ trợ nhiều nhà sản xuất (chủ đề sử dụng nó) với một người tiêu dùng và cách khác là cách khác.

Tôi không hiểu thực hiện để sử dụng. Ai đó có thể giải thích sự khác biệt là gì?

Ngoài ra, "chính sách công bằng tùy chọn" trong là ArrayBlockingQueuegì?


1
Bạn cũng quên hỏi về PriorityBlockingQueue, điều này rất hữu ích cho việc chỉ định một đơn hàng trong đó các luồng được xử lý.
IgorGanapolsky

Câu trả lời:


53

Về cơ bản sự khác biệt giữa chúng là đặc điểm hiệu suất và hành vi chặn.

Lấy thứ dễ nhất trước, ArrayBlockingQueuelà một hàng có kích thước cố định. Vì vậy, nếu bạn đặt kích thước ở mức 10 và cố gắng chèn phần tử thứ 11, câu lệnh chèn sẽ chặn cho đến khi một luồng khác loại bỏ một phần tử. Vấn đề công bằng là những gì xảy ra nếu nhiều luồng cố gắng chèn và xóa cùng một lúc (nói cách khác trong khoảng thời gian khi Hàng đợi bị chặn). Một thuật toán công bằng đảm bảo rằng luồng đầu tiên yêu cầu là luồng đầu tiên nhận được. Mặt khác, một luồng đã cho có thể đợi lâu hơn các luồng khác, gây ra hành vi không thể đoán trước (đôi khi một luồng sẽ chỉ mất vài giây vì các luồng khác bắt đầu sau đó đã được xử lý trước). Sự đánh đổi là cần có chi phí để quản lý sự công bằng, làm chậm thông lượng.

Sự khác biệt quan trọng nhất giữa LinkedBlockingQueueConcurrentLinkedQueuelà nếu bạn yêu cầu một phần tử từ a LinkedBlockingQueuevà hàng đợi trống, luồng của bạn sẽ đợi cho đến khi có một cái gì đó ở đó. A ConcurrentLinkedQueuesẽ trở lại ngay với hành vi của một hàng đợi trống.

Cái nào phụ thuộc vào nếu bạn cần chặn. Nơi bạn có nhiều nhà sản xuất và một người tiêu dùng, có vẻ như nó. Mặt khác, nơi bạn có nhiều người tiêu dùng và chỉ có một nhà sản xuất, bạn có thể không cần hành vi chặn và có thể rất vui khi người tiêu dùng kiểm tra xem hàng đợi có trống không và tiếp tục nếu có.


67
Trả lời là sai lệch. Cả LinkedBlockingQueue và ConcurrencyLinkedQueue đều có phương thức "poll ()" để loại bỏ phần đầu của hàng đợi hoặc trả về null (không chặn) và phương thức "Offer (E e)" chèn vào đuôi hàng đợi và không chặn. Sự khác biệt là chỉ LinkedBlockingQueue đã chặn các hoạt động ngoài đến hoạt động non-blocking - và cho đặc ân đó, bạn phải trả mức giá mà LinkedBlockingQueue thực sự có một số khóa. Câu trả lời khác giải thích điều này.
Khỏa thân

123

Đồng thờiLinkedQueue có nghĩa là không có khóa nào được thực hiện (nghĩa là không có cuộc gọi được đồng bộ hóa (này) hoặc Lock.lock ). Nó sẽ sử dụng thao tác CAS - So sánh và Hoán đổi trong quá trình sửa đổi để xem nút đầu / đuôi có còn giống như khi bắt đầu không. Nếu vậy, hoạt động thành công. Nếu nút đầu / đuôi khác nhau, nó sẽ quay xung quanh và thử lại.

LinkedBlockingQueue sẽ khóa trước khi sửa đổi. Vì vậy, các cuộc gọi ưu đãi của bạn sẽ chặn cho đến khi họ nhận được khóa. Bạn có thể sử dụng quá tải ưu đãi mất một TimeUnit để nói rằng bạn chỉ sẵn sàng đợi X lượng thời gian trước khi từ bỏ add (thường tốt cho hàng đợi loại tin nhắn trong đó tin nhắn đã cũ sau X số mili giây).

Công bằng có nghĩa là việc thực hiện Khóa sẽ giữ cho các luồng được ra lệnh. Có nghĩa là nếu Chủ đề A nhập và sau đó Chủ đề B nhập, Chủ đề A sẽ nhận được khóa đầu tiên. Không có sự công bằng, nó không thực sự được xác định những gì xảy ra. Nó rất có thể sẽ là chủ đề tiếp theo được lên lịch.

Đối với cái nào để sử dụng, nó phụ thuộc. Tôi có xu hướng sử dụng ConcurrencyLinkedQueue vì thời gian các nhà sản xuất của tôi mất công để đưa vào hàng đợi rất đa dạng. Tôi không có nhiều nhà sản xuất sản xuất cùng một lúc. Nhưng phía người tiêu dùng phức tạp hơn vì cuộc thăm dò ý kiến ​​sẽ không đi vào trạng thái ngủ ngon. Bạn phải tự xử lý nó.


1
Và trong những điều kiện nào ArrayBlockingQueue tốt hơn LinkedBlockingQueue?
kolobok

@akapelko ArrayBlockingQueue cho phép đặt hàng chi tiết hơn.
IgorGanapolsky

2
Điều đó có nghĩa là gì - "nó sẽ quay xung quanh và thử lại." ?
Lester

9

Tiêu đề câu hỏi của bạn đề cập đến Chặn hàng đợi. Tuy nhiên, ConcurrentLinkedQueuekhông một hàng đợi chặn.

Các BlockingQueues là ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, và SynchronousQueue.

Một số trong số này là rõ ràng không phù hợp với mục đích của bạn ( DelayQueue, PriorityBlockingQueueSynchronousQueue). LinkedBlockingQueueLinkedBlockingDequegiống hệt nhau, ngoại trừ cái sau là Hàng đợi hai đầu (nó thực hiện giao diện Deque).

ArrayBlockingQueuechỉ hữu ích nếu bạn muốn giới hạn số lượng phần tử, tôi sẽ sử dụng LinkedBlockingQueue.


Tôi đã xóa từ Chặn khỏi tiêu đề, cảm ơn. Hãy để tôi xem nếu tôi đã nhận nó, những gì bạn nói có nghĩa là LinkedBlockingQueue có thể được sử dụng trong nhiều người tiêu dùng / sản xuất các kịch bản trên cùng một đối tượng?
David Hofmann

1
Tôi nghĩ rằng ArrayBlockingQueue cho phép sắp xếp các chủ đề chi tiết hơn? Do đó lợi thế của nó.
IgorGanapolsky

4

ArrayBlockingQueue có dung lượng bộ nhớ thấp hơn, nó có thể sử dụng lại nút phần tử, không giống như LinkedBlockingQueue phải tạo một đối tượng $ Node LinkedBlockingQueue cho mỗi lần chèn mới.


1
điểm tốt! tôi thích ArrayBlockingQueue hơn LinkedBlockingQueue
nghìn tỷ vào

2
Điều này không nhất thiết đúng - nếu hàng đợi của bạn gần như trống rỗng rất nhiều thời gian nhưng cần phải có khả năng phát triển lớn, thì ArrayBlockingQueuesẽ có dấu chân bộ nhớ tồi tệ hơn nhiều - trong khi đó, nó vẫn có một mảng lớn được phân bổ trong bộ nhớ những LinkedBlockingQueuesẽ có một bộ nhớ không đáng kể khi gần để trống.
Kreas

1
  1. SynchronousQueue(Lấy từ một câu hỏi khác )

SynchronousQueuelà nhiều hơn một bàn giao, trong khi LinkedBlockingQueuechỉ cho phép một yếu tố duy nhất. Sự khác biệt là put()cuộc gọi đến SynchronousQueuesẽ không trả lại cho đến khi có take()cuộc gọi tương ứng , nhưng với LinkedBlockingQueuekích thước 1, put()cuộc gọi (đến hàng đợi trống) sẽ trả về ngay lập tức. Về cơ bản, đó là BlockingQueueviệc triển khai khi bạn không thực sự muốn xếp hàng (bạn không muốn duy trì bất kỳ dữ liệu đang chờ xử lý nào).

  1. LinkedBlockingQueue( LinkedListTriển khai nhưng không chính xác JDK Triển khai LinkedListsử dụng Node lớp bên trong tĩnh để duy trì Liên kết giữa các phần tử)

Trình xây dựng cho LinkedBlockingQueue

public LinkedBlockingQueue(int capacity) 
{
        if (capacity < = 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node< E >(null);   // Maintains a underlying linkedlist. ( Use when size is not known )
}

Lớp nút được sử dụng để duy trì liên kết

static class Node<E> {
    E item;
    Node<E> next;
    Node(E x) { item = x; }
}

3. ArrayBlockingQueue (Thực hiện mảng)

Trình xây dựng cho ArrayBlockingQueue

public ArrayBlockingQueue(int capacity, boolean fair) 
{
            if (capacity < = 0)
                throw new IllegalArgumentException();
            this.items = new Object[capacity]; // Maintains a underlying array
            lock = new ReentrantLock(fair);
            notEmpty = lock.newCondition();
            notFull =  lock.newCondition();
}

IMHO Sự khác biệt lớn nhất giữa ArrayBlockingQueueLinkedBlockingQueuerõ ràng từ nhà xây dựng, người ta có cấu trúc dữ liệu cơ bản Mảng và danh sách liên kết khác .

ArrayBlockingQueuesử dụng thuật toán điều kiện kép khóa đơnLinkedBlockingQueuelà biến thể của thuật toán "hai hàng đợi khóa" và nó có 2 khóa 2 điều kiện (TakeLock, putLock)


0

Đồng thờiLinkedQueue là không khóa, LinkedBlockingQueue thì không. Mỗi lần bạn gọi LinkedBlockingQueue.put () hoặc LinkedBlockingQueue.take (), trước tiên bạn cần có được khóa. Nói cách khác, LinkedBlockingQueue có tính đồng thời kém. Nếu bạn quan tâm đến hiệu suất, hãy thử ConcurrencyLinkedQueue + LockSupport.

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.