Home > other >  Problem with Java semaphores (tryAcquire)
Problem with Java semaphores (tryAcquire)

Time:07-06

I'm trying to produce the AAB AAB sequence with this code:

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.tryAcquire()) {
                semB.release();
            }
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

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

In this code the final result is "A".

From what I know tryAcquire () should immediately return true or false as the case may be, instead it seems to respond late. How can we solve this problem?

CodePudding user response:

This is primarily an explanation of your issue and an approach of analysis - but I did see what you are attempting and added a simple fix.

It might be best to use a table as you walk through the logic. Note that the order of execution of P1A and P2B may vary and so this is one sequence - but I'm certain all sequences lead to the same outcome described:

|     P1A     | semA |     P2B     | semB | Output |
| ----------- | ---- | ----------- | ---- | ------ |
| [1] started |      |             |      |        |
|             |   2  |             |   0  |        |
| [1] acq  A  |   1  |             |   0  |        |
|             |      |             |      |    A   |
| [1] acqt A  |   0  |             |   0  |        |
|             |      | [1] started |   0  |        |
|             |   0  | [1] blocked |   0  |        |
| [1] end     |   0  |             |   0  |        |
| [2] started |   0  |             |   0  |        |
| [2] blocked |   0  |             |      |        |
|             |   0  | [2] started |   0  |        |
|             |   0  | [2] blocked |   0  |        |

ad infinitum.

It seems starting new threads on each iteration only complicates the results - as opposed to having each threads loop forever on its own.

Since semA is initialized with 2 permits, the tryAcquire will be successful (true) and so the !tryAcquire is false.

Update P1A

And now that I see what you are attempting, I think the following is more direct to your approach. Just have P1A do this (leave P2B as is):

public void run() {
        if (semA.tryAcquire()) {
           System.out.print("A");
        } else {
            semB.release();
        }
}

With your regard to your main loop - I can't say it's wrong except it does not wait for the threads to complete (join) which can only lead to confusing results. So either join both threads (instead of sleep) or move the forever loops into the threads.

  • Related