Я бы посоветовал сделать некую реализацию Condition.
Грубо говоря, при локе запоминать все потоки которые пришли к нам, а лучше связывать их в очереди, и при освобождении посылать сигнал о том, что у нас есть свободное место и брать первый из этой очереди. Вообще советую посмотреть в AbstractQueuedSynchronizer методы await и signal.
После примерной вашей реализации такого же механизма будет все довольно просто)
При поступлении в очередь вы проверяете есть ли место, если есть то прост добавляем в очередь, если нету, то паркуем поток через аналог метода await, при выходе с метода, шлем signal, чтобы в вашу очередь зашел первый припаркованный поток.
Можно конечно попробовать проще, сделать sychronized метод добавления в очередь, и если у нас размер больше не отпускать его, сделать бесконечный цикл. А как только появится место, то отпустить)
Решайте сами как лучше.)