«Как это можно улучшить?»
Что ж, вам нужно посмотреть на каждый метод в вашем классе и подумать, что произойдет, если другой поток одновременно вызовет этот метод или любой другой метод. Например, вы устанавливаете блокировку в методе Remove, но не в методе Add. Что происходит, если один поток добавляет одновременно с удалением другого потока? Плохие вещи.
Также учтите, что метод может возвращать второй объект, который обеспечивает доступ к внутренним данным первого объекта, например, GetEnumerator. Представьте, что один поток проходит через этот перечислитель, другой поток одновременно изменяет список. Не хорошо.
Хорошее эмпирическое правило состоит в том, чтобы упростить эту задачу, чтобы сократить число методов в классе до абсолютного минимума.
В частности, не наследуйте другой контейнерный класс, потому что вы будете выставлять все методы этого класса, предоставляя вызывающей стороне способ повредить внутренние данные или увидеть частично завершенные изменения данных (так же плохо, потому что данные кажутся поврежденными в этот момент). Скройте все детали и будьте совершенно безжалостны о том, как вы разрешаете доступ к ним.
Я бы настоятельно рекомендовал вам использовать готовые решения - получить книгу о многопоточности или использовать стороннюю библиотеку. В противном случае, учитывая то, что вы пытаетесь, вы будете долго отлаживать свой код.
Кроме того, не имеет ли смысла для Remove возвращать элемент (скажем, тот, который был добавлен первым, поскольку он является очередью), а не вызывающий объект, выбирающий конкретный элемент? И когда очередь пуста, возможно, удаление также должно блокировать.
Обновление: ответ Марка фактически реализует все эти предложения! :) Но я оставлю это здесь, так как может быть полезно понять, почему его версия является таким улучшением.