Unfair semaphore in Java
I don't like using the object as a mutex and Condition variable. Doing that would let another programmer spin up a thread to lock and never release the Semaphore.

Instead you should either provide a private Object lock = new Object(); and sync on that or use the java.util.concurrent.locks.ReentrantLock and its condition:

private final Lock mutex = new ReentrantLock();
private final Condition condition = mutex.newCondition();

public void acquire() throws InterruptedException {
    mutex.lockInterruptibly();//let interrupt throw as soon as possible
    try{
        while (count &lt;= 0) {
            condition.await();
        }
        --count;
    } finally {
        mutex.unlock();
    }
}

// Implementation
public void release() {
    mutex.lock();
    try{
        ++count;
        if (count &gt; 0) {
            condition.signal();
        }
    } finally {
        mutex.unlock();
    }
}


This makes it easy to change the semaphore to a fair one by passing true to the constructor.

Though I expect you will want to implement the wait queue yourself as an example.

If you want to go a step lower and no use the pre-implemented locks can instead use LockSupport which lets to park the current thread and unpark it from another thread safely.