Data Structures & AlgorithmsMedium
Simulate a multithreaded producer-consumer problem using Java threads and semaphores. Implement a producer that generates items and adds them to a shared buffer, and a consumer that retrieves and processes items from the same buffer. Synchronize access to the buffer using semaphores to prevent race conditions and ensure that the producer doesn't add items to a full buffer and the consumer doesn't try to retrieve items from an empty buffer. Specifically: Define a shared buffer: This could be an ArrayList or any other suitable data structure with a fixed capacity. Implement the Producer: The producer should generate a series of items (e.g., integers) and attempt to add them to the buffer. Before adding an item, it must acquire a semaphore that represents available space in the buffer. If no space is available, the producer should wait. After adding an item, it should release a semaphore that represents available items in the buffer. Implement the Consumer: The consumer should attempt to retrieve and process items from the buffer. Before retrieving an item, it must acquire a semaphore that represents available items in the buffer. If no items are available, the consumer should wait. After retrieving an item, it should release a semaphore that represents available space in the buffer. Synchronization: Use semaphores to handle synchronization. One semaphore should track the number of empty slots in the buffer, and another should track the number of filled slots. Example Scenario: Create a buffer with a capacity of 5. Have the producer generate 10 items and the consumer consume 10 items. Print messages indicating when the producer adds an item and when the consumer consumes an item. Ensure that the producer and consumer run concurrently and that no items are lost or duplicated. Your solution should demonstrate: Proper use of Java threads. Correct semaphore usage for synchronization. Prevention of race conditions. Handling of full and empty buffer conditions.