Home > Software engineering >  Sequence java with Semaphores
Sequence java with Semaphores

Time:07-05

I'm trying to get the AABBAABB sequence to infinity but what I get is AABBAAABB. The java code is the following:

private static Semaphore semA = new Semaphore(2);
private static Semaphore semB = new Semaphore(0);

public static void main(String[] args) {
    while(true) {
        new P1A().start();
        new P2B().start();
        try {
            Thread.sleep(1000);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

}

static class P1A extends Thread{
    
    public void run() {
        try {
            semA.acquire();
            System.out.print("A");
            if(semA.availablePermits() == 0) {
                semB.release(2);
            }
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

static class P2B extends Thread{
    
    public void run() {
        try {
            semB.acquire();
            System.out.print("B");
            if(semB.availablePermits() == 0)
                semA.release(2);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

According to my code, once printed A twice it should suspend Thread P1A for Thread P2B to run two more times. But I can't get it despite the traffic lights release 2 seats ...

Can you help me?

CodePudding user response:

Here's what is happening in your code:

  1. Loop starts. Threads: A1, B1 are created.
  2. A1 prints "A". B1 waits for a permit.
  3. Loop starts again after sleeping. Threads: A2, B2 are created.
  4. A2 prints "A" - number of available permits in the semaphore A is 0, so 2 permits are released to the B semaphore. B2 waits for a permit (yet).
  5. B1 and B2 both acquire a permit and print "B". Since both threads acquired the permit, number of available permits is 0 - when both threads check that condition, both release 2 permits in the semaphore A. Semaphore A has 4 permits at this point, which cause the printed sequence to fall apart: AABBAAAABB....

Race between B threads

After adding thread identifiers to the print statements and adding additional logging after releasing permits the whole run looks like this (println used instead of print for better readability):

A (Thread-A1)
A (Thread-A2)
B (Thread-B1)
B (Thread-B2)
Thread-A2 released B
Thread-B1 released A
Thread-B2 released A
A (Thread-A3)
A (Thread-A4)
A (Thread-A5)
A (Thread-A6)
Thread-A6 released B
B (Thread-B3)
Thread-B3 released A
B (Thread-B4)
Thread-B4 released A

There may of course multiple ways of resolving that, one of which (I'd assume not the best, but working) could be a synchronized verification of the target state (Semaphore references have to be declared as final):

if (semA.availablePermits() == 0) {
    synchronized (semB) {
        if (semB.availablePermits() == 0) {
            semB.release(2);
        }
    }
}

Thanks to the synchronization, each thread that reaches the inner if has access to the actual ("target") state - no parallel modification is possible in this case.

The behavior above is a compare-and-swap (CAS) operation - we verify if current value is a value we expect and if so, we act on it; otherwise we skip execution of the if body. Semaphore actually partially uses this mechanism underneath (Semaphore.Sync inner class), but it retries the incrementation until successful (or throws after overflowing):

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current   releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}

I've created a GitHub repository where you can find the code for the logging result above (InitialWithLogging), a fixed code with comments (InitialFixedWithComments) and a refactored code that strictly represents the abstraction without duplication (SimplifiedSolution).

  • Related