# Thread safe queue manager

Implement a thread-safe Java class QueueManager that manages multiple named queues. The class must support concurrent access from multiple threads.

## Required Methods
Your QueueManager class must implement the following methods:

```
add(String queueName, String... items)
```
Adds one or more items to the specified queue. All items are stored as strings.

If the queue does not exist, create it automatically.
Example: add("master", "2", "10.2", "Text") creates queue "master" (if it doesn't exist) and adds three items.

```
pop(String queueName)
```
Removes and returns the first item from the queue (FIFO - first in, first out).

Throws QueueEmptyException if the queue is empty.
Throws QueueDoesNotExistException if the queue does not exist.

```
clear(String queueName)
```
Removes all items from the queue, making it empty. The queue object itself remains.

Throws QueueDoesNotExistException if the queue does not exist.

```
getQueueContents(String queueName)
```
Returns a list of all items in the queue in FIFO order (first item added appears first).

Throws QueueDoesNotExistException if the queue does not exist.

```
getAllQueueNames()
```
Returns a collection of all queue names currently managed.

```
sortQueue(String queueName)
```
Sorts the items in the specified queue alphabetically (as strings).

Throws QueueDoesNotExistException if the queue does not exist.

```
deleteQueue(String queueName)
```
Removes the queue entirely from the manager.

Throws QueueDoesNotExistException if the queue does not exist.
Thread-Safety Requirements


--------------------------------------------------------------------------------

CRITICAL: Your implementation must be thread-safe.

REQUIRED: Implement "fine-grained thread-safety" so that:

Operations on different queues can proceed concurrently without blocking each other
Operations on the same queue are properly synchronized
Creating new queues is thread-safe
Reading the list of all queues is thread-safe
Example scenario that must work concurrently:

 - Thread 1 adds items to queue "A"
 - Thread 2 adds items to queue "B"
 - Thread 3 pops from queue "C"

These operations should NOT block each other since they operate on different queues.

## Custom Exceptions
Create the following custom exceptions:

 - QueueEmptyException - thrown when attempting to pop from an empty queue
 - QueueDoesNotExistException - thrown when referencing a non-existent queue


## Implementation Tips
Consider using:

 - ConcurrentHashMap for storing queues
 - Per-queue locks (e.g., a lock object associated with each queue)
 - Proper synchronization for queue creation
 - Defensive copying when returning queue contents