I'm really a new at Java concurrency and I'm trying to implement the following specifications:
This is not the exact specification, but the only problem that I have is the following:
I'm not able to make the car skip the turn. If two cars pick the same spots, then one is parked and the other is waiting until the park is free. Which is not the bahvior that I want. My first idea was to simply synchronize the read and write to a variable occupied:
class Car implements Runnable {
private CarState state = CarState.driving
run {
while(true) {
switch(state) {
case driving:
System.out.println(this + " driving.");
state = parking;
break;
case parking: Spot s = CarPark.getRandomSpot();
if(s.willingToPark(this)) {
System.out.println(s + " occupied. " + this
+ " skip park turn.");
} else {
s.park(this);
}
state = driving;
}
}
}
}
class Spot {
private boolean occupied = false;
private Car owner = new Car(...);
synchronized boolean willingToPark(Car c) {
if(occupied) {
return true;
} else {
occupied = true;
return false;
}
synchronized void park(Car c) {
System.out.println(c + " parking at " + this);
//don't care how this is implemented, just keep in mind
//that it will enter in a loop until the owner came back.
occupied = false;
}
}
If I run this with three cars, then I will end up in having car0 is parking at spot1, car1 is parking at spot0, car2 is waiting on spot0, because car1 is executing the synchronized block park(Car c). I don't get how is it possible that two cars can park in the same spot if the willingToPark is synchronized.
Thank you
I don't get how is it possible that two cars can park in the same spot if the willingToPark is synchronized.
It's actually simple. Car1 captures spot0 and starts waiting for owner in a loop inside park()
method (you did not provide the code). While it is waiting, it owns monitor and does not allow anyone to call synchronized methods on spot0.
That is the reason why car2 hangs on willingToPark()
method.
The problem is in the looping of park
. Imagine this:
s
.s
; only one will succeed.park
to spin, while still holding the lock.There's nothing (short of killing the JVM) that will tell the thread that failed to park, to stop waiting on s
. It'll wait until it can acquire the lock -- which will only happen once the first thread finishes the loop on park
.
The solution is not to loop in park
at all. Instead, the Car should unset the occupied
flag, via a new method unpark(Car)
. This method must also be synchronized, for the sake of memory visibility across threads.
Now the data flow looks like:
s
.s
; only one will succeed.occupied = true
, and immediately returns and then releases the lock on s
s
, and sees that the spot is occupied.Incidentally, you don't even need a synchronized
method for this. You can use an AtomicBoolean's compareAndSet method, which lets you atomically check the AtomicBoolean's value, and only set it if its current value is what you expect it to be. Thus, return occupied.compareAndSet(false, true)
means "atomically check the current value; if it's false, then set it to true and return true; if it's true, then keep it as it is, and return false." This kind of behavior is useful, but a bit more advanced.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With