Reputation: 8325
EDIT:
This question is about resolving a problem using only Java code. The problem is caused by SQLLite indirectly but cannot be solved by changing Database system or with SQL code. I mention SQLLite because otherwise users would point to useless solutions that actually breaks project imposed requirments (A defined user interface and behaviour and SQLite as DBMS because it can run without server and projects are subject to automatic correction).
EDIT2:
The deadlock happens on Java side, it is not easy to see it, I debugged for a whole day before realizing that, forget about SQLLite, I need to find a way to get Parking working like a monitor but without conflicting with synchronized
causing a deadlock
Currently I have the following situation:
I Have a simplified Parking class, actually it is a monitor where clients call the lendVehicle
method from other threads.
public class Parking{
private final long parkId;
private final ParkingAPI sqlLayer;
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
public Parking( long mparkId, ParkingAPI api){
sqlLayer = api;
parkId = mparkId;
}
long lendVehicle(){
lock.lock();
try{
while(sqlLayer.countVehicles(parkId) == 0)
notEmpty.await();
return sqlLayer.lend(parkId);
} finally{
lock.unlock();
}
}
void giveBackVehicle(long vehicleId){
lock.lock();
try{
sqlLayer.giveBack(vehicleId,parkId);
notEmpty.signal();
} finally{
lock.unlock();
}
}
When I mock the SQL layer with just an atomic counter, the class works perfectly, however since the application is using SQL Lite I have to protect the connection from concurrent access (basically I can execute 1 query at any given time because of SQL Lite).
Currently the code is synchronized
over the DBLayer
object (wich is shared across all classes).
class ParkingQuery implements ParkingAPI{
private final DBLayer connection;
public SQLLayer(DBLayer db){
connection = db;
}
@Override
int lend(long parkId){
synchronized( connection){
return connection.lendVehicleFromPark(parkId);
}
}
@Override
int countVehicles(long parkId){
synchronized( connection){
return connection.countVehiclesQuery(parkId);
}
}
@Override
void giveBack(long vehicleId, long parkId){
synchronized( connection){
connection.giveBackVehicle(parkId, vehicleId);
}
}
}
The problem is the synchronized part, wich does not play well with the Parking's monitor: wich in fact cause a deadlock.
How can I preserve functionality of parking? (Can't remove synchronized on ParkingQuery because SQLite just explode if queries are not synchronized and bad things starts happening).
Note that concurrent access to SQLLite is mandatory because that's a school project.
EDIT: Desired parking behaviour: If a user desire lending a vehicle and it is not available the user have to wait someone else returns back a vehicle from lending.
Upvotes: 1
Views: 134
Reputation: 24336
This code:
long lendVehicle(){
lock.lock();
try{
...
}
is wrong. You basically lock then it is not guaranteed to unlock because it is outside of the try
block. Try to break down the problem more appropriately. You have a producer, which is the giving back of the car and then your consumer is lending the car. So you know that your "car lot" only can hold N cars (lets say 10 for example sake). So if your buffer (car lot) is full you don't want to enable more cars to be given back (think about trying to park in a garage where there are no spots left).
Now you can simply check in your return car function if the buffer is full and if it is ignore the operation. When lending the car you want to make sure the lot is not empty.
Upvotes: 2