JavaRush /Blog Java /Random-VI /Ví dụ SynchronousQueue trong Java - giải quyết vấn đề Nhà...
profeg
Mức độ

Ví dụ SynchronousQueue trong Java - giải quyết vấn đề Nhà sản xuất Người tiêu dùng

Xuất bản trong nhóm
Ví dụ SynchronousQueue trong Java - giải quyết vấn đề Nhà sản xuất Người tiêu dùng
SynchronousQueue là một loại BlockingQueue đặc biệt trong đó mỗi thao tác chèn phải chờ lệnh xóa tương ứng trong một luồng khác và ngược lại. Khi bạn gọi phương thức put() trên SynchronousQueue, nó sẽ chặn cho đến khi một luồng khác lấy phần tử đó từ nó. Theo đó, nếu một luồng khác cố gắng loại bỏ một phần tử khỏi nó và phần tử đó không có ở đó thì luồng đó sẽ chặn cho đến khi luồng kia đưa phần tử đó vào hàng đợi. Bạn có thể hình dung SynchronousQueue như một vận động viên ( sợi dây ) chạy với ngọn đuốc Olympic, anh ta chạy với ngọn đuốc (vật đang được truyền qua) và chuyền nó cho một vận động viên khác đang đợi ở phía bên kia. Nếu để ý đến cái tên, bạn sẽ hiểu SynchronousQueue được đặt tên như vậy là có lý do, nó truyền dữ liệu một cách đồng bộ sang một luồng khác ; nó đợi ai đó lấy dữ liệu thay vì chỉ đưa dữ liệu vào và thoát ra (một hoạt động không đồng bộ). Nếu bạn quen thuộc với CSP và Ada thì bạn biết rằng hàng đợi được đồng bộ hóa tương tự như cuộc gặp gỡ của các luồng. Chúng rất phù hợp cho các cấu trúc chuyển giao điều khiển trong đó một đối tượng chạy trong một luồng phải đồng bộ hóa với một đối tượng trong một luồng khác để truyền một số thông tin, sự kiện hoặc tác vụ tới nó. Trong các hướng dẫn lập trình đa luồng trước đây, chúng ta đã học cách giải quyết vấn đề nhà sản xuất-người tiêu dùng bằng cách sử dụng các phương thức chờ và thông báo cũng như BlockingQueue . Bây giờ chúng ta sẽ tìm hiểu cách áp dụng mẫu nhà sản xuất-người tiêu dùng bằng SynchronousQueue. Lớp này cũng hỗ trợ hành vi công bằng trong việc ra lệnh chờ đợi của các luồng sản xuất và tiêu dùng. Theo mặc định, thứ tự này không được đảm bảo. Tuy nhiên, hàng đợi được tạo bằng các thuộc tính hợp lý sẽ đảm bảo quyền truy cập cho các luồng trong hàng đợi FIFO (Firs In First Out).
Nhà sản xuất/Người tiêu dùng sử dụng SynchronousQueue trong Java.
Ví dụ SynchronousQueue trong Java - giải quyết vấn đề Nhà sản xuất Người tiêu dùng - 1 Như tôi đã nói ở trên, không có gì tốt hơn vấn đề nhà sản xuất-người tiêu dùng để hiểu giao tiếp giữa các luồng trong bất kỳ ngôn ngữ lập trình nào. Trong vấn đề này, một luồng đóng vai trò là nhà sản xuất tạo ra các sự kiện và nhiệm vụ, còn luồng kia đóng vai trò là người tiêu dùng của nó. Bộ đệm dùng chung được sử dụng để truyền dữ liệu từ nhà sản xuất đến người tiêu dùng. Khó khăn trong việc giải quyết vấn đề này xuất hiện trong những trường hợp cực đoan, chẳng hạn như khi nhà sản xuất buộc phải chờ đợi vì... bộ đệm đã đầy hoặc người tiêu dùng buộc phải chờ vì bộ đệm trống. Chuyện này được giải quyết dễ dàng vì... Hàng đợi chặn không chỉ cung cấp bộ đệm để lưu trữ dữ liệu mà còn cung cấp khả năng kiểm soát luồng, chặn luồng gọi phương thức put() (Nhà sản xuất) nếu bộ đệm đầy và chặn luồng gọi phương thức take() (Người tiêu dùng) nếu bộ đệm trống. Bây giờ chúng ta sẽ giải quyết vấn đề tương tự bằng cách sử dụng SynchronousQueue, một loại bộ sưu tập song song đặc biệt có dung lượng bằng 0. Trong ví dụ sau, chúng ta có hai luồng tên là PRODUCERCONSUMER (luôn đặt tên cho các luồng, đây là một kiểu lập trình đa luồng rất hay). Luồng đầu tiên đăng điểm trong trò chơi và luồng thứ hai tiêu thụ điểm đó. Điểm số trong trò chơi không gì khác hơn là một đối tượng thuộc loại String. Nhưng nếu bạn chạy chương trình với loại khác, bạn sẽ không nhận thấy bất kỳ sự khác biệt nào. Để hiểu cách SynchronousQueue hoạt động và cách giải quyết vấn đề nhà sản xuất-người tiêu dùng, bạn cần: chạy chương trình để gỡ lỗi (gỡ lỗi) trong môi trường Eclipse hoặc đơn giản là khởi động chuỗi sản xuất bằng cách nhận xét Consumer.start(); nếu luồng tiêu dùng không chạy thì luồng sản xuất sẽ bị chặn tại queue.put(event); nếu đang chạy, bạn sẽ không thể thấy nhà sản xuất [NHÀ SẢN XUẤT] xuất bản sự kiện :FOUR. Điều này xảy ra bởi vì hành vi cụ thể của SynchronousQueue, đảm bảo rằng dữ liệu đăng của luồng sẽ bị chặn cho đến khi một luồng khác lấy dữ liệu và ngược lại. Bạn có thể kiểm tra phần còn lại của mã bằng cách nhận xét production.start(); và chỉ bắt đầu chuỗi tiêu dùng. Nếu bạn nghiên cứu kỹ những gì chương trình đưa ra, bạn sẽ nhận thấy rằng thứ tự đầu ra bị đảo ngược. Có vẻ như luồng [TIÊU DÙNG] đã lấy dữ liệu trước khi luồng [NHÀ SẢN XUẤT] tạo ra dữ liệu đó. Điều này là do SynchronousQueue không đảm bảo xếp hàng theo mặc định. Nhưng nó có các quy tắc công bằng đặt quyền truy cập vào các luồng theo thứ tự FIFO. Bạn có thể kích hoạt các quy tắc này bằng cách chuyển true cho hàm tạo SynchronousQueue bị quá tải như thế này: import java.util.concurrent.SynchronousQueue; /** * Java Program to solve Producer Consumer problem using SynchronousQueue. A * call to put() will block until there is a corresponding thread to take() that * element. * * @author Javin Paul */ public class SynchronousQueueDemo{ public static void main(String args[]) { final SynchronousQueue queue = new SynchronousQueue (); Thread producer = new Thread("PRODUCER") { public void run() { String event = "FOUR"; try { queue.put(event); // thread will block here System.out.printf("[%s] published event : %s %n", Thread .currentThread() .getName(), event); } catch (InterruptedException e) { e.printStackTrace(); } } }; producer.start(); // starting publisher thread Thread consumer = new Thread("CONSUMER") { public void run() { try { String event = queue.take(); // thread will block here System.out.printf("[%s] consumed event : %s %n", Thread .currentThread() .getName(), event); } catch (InterruptedException e) { e.printStackTrace(); } } }; consumer.start(); // starting consumer thread } } Output: [CONSUMER] consumed event : FOUR [PRODUCER] published event : FOUR new SynchronousQueue(boolean fair).
Những điều bạn cần nhớ về SynchronousQueue trong Java.

Dưới đây là một số thuộc tính quan trọng của loại hàng đợi chặn đặc biệt này trong Java. Việc truyền dữ liệu từ luồng này sang luồng khác một cách đồng bộ là rất hữu ích. Hàng đợi này không có dung lượng và bị chặn cho đến khi một luồng khác giải phóng nó.

  1. Các khối SynchronousQueue và cho đến khi một luồng sẵn sàng lấy dữ liệu, một luồng khác sẽ cố gắng đưa dữ liệu vào.
  2. SynchronousQueue không có âm lượng. Tức là nó không chứa dữ liệu.
  3. SynchronousQueue được sử dụng để triển khai chiến lược xếp hàng chuyển tiếp, trong đó một luồng chuyển điều khiển đến một luồng đang chờ hoặc tạo một luồng mới nếu được phép, nếu không thì điều khiển sẽ không được chuyển.
  4. Hàng đợi này không cho phép dữ liệu null. Việc cố gắng thêm phần tử null sẽ tạo ra ngoại lệ NullPointerException .
  5. Nếu bạn sử dụng các phương thức khác từ Bộ sưu tập (chẳng hạn như chứa), SynchronousQueue sẽ hoạt động giống như một bộ sưu tập trống.
  6. Bạn không thể sử dụng phương thức xem nhanh của SynchronousQueue vì phần tử chỉ tồn tại khi bạn cố xóa nó; Ngoài ra, bạn sẽ không thể chèn các phần tử (sử dụng bất kỳ phương thức nào) cho đến khi một luồng khác cố gắng xóa nó.
  7. Bạn sẽ không thể sử dụng iterator cho SynchronousQueue vì... nó không có phần tử.
  8. SynchronousQueue có thể được tạo bằng các quy tắc công bằng, trong đó quyền truy cập vào các luồng được đảm bảo theo thứ tự FIFO.
Có lẽ đây là tất cả về SynchronousQueue trong Java. Chúng tôi đã xem xét một số tính năng đặc biệt của bộ sưu tập đa luồng này và tìm hiểu cách giải quyết vấn đề cổ điển giữa người sản xuất và người tiêu dùng bằng cách sử dụng SynchronousQueue trong Java. Nhân tiện, gọi nó là Hàng đợi là không hoàn toàn chính xác, bởi vì... nó không chứa các phần tử. Lệnh gọi put() sẽ không hoàn thành cho đến khi một luồng khác gọi hàm take(). Sẽ đúng hơn nếu coi nó như nơi gặp gỡ của các chủ đề, nơi chúng chia sẻ một đối tượng. Nói cách khác, nó là một tiện ích để truyền đồng bộ các đối tượng trong Java, có lẽ là một giải pháp thay thế an toàn hơn cho phương thức chờ và thông báo .
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION