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.