【问题标题】:Java: Help design out deadlock caused by SQLLiteJava:帮助设计出由 SQLite 引起的死锁
【发布时间】:2019-05-29 22:39:38
【问题描述】:

编辑:

这个问题是关于解决一个仅使用 Java 代码的问题。该问题是由 SQLLite 间接引起的,但不能通过更改数据库系统或使用 SQL 代码来解决。我提到 SQLLite 是因为否则用户会指向实际上破坏项目强加要求的无用解决方案(定义的用户界面和行为以及作为 DBMS 的 SQLite,因为它可以在没有服务器的情况下运行,并且项目会自动更正)。

EDIT2: 死锁发生在Java端,不容易看到,我调试了一整天才意识到,忘记SQLLite,我需要找到一种方法让Parking像监视器一样工作但是不与synchronized 冲突导致死锁


目前我有以下情况:

我有一个简化的停车类,实际上它是一个监视器,客户端从其他线程调用lendVehicle 方法。

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();
        }
    }

当我只用一个原子计数器模拟 SQL 层时,该类可以完美运行,但是由于应用程序使用的是 SQL Lite,我必须保护连接免受并发访问(基本上我可以在任何给定时间执行 1 个查询,因为SQL 精简版)。

目前代码是synchronized 上的DBLayer 对象(所有类共享)。

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);
        }
    }
}

问题在于同步部分,它不能很好地与停车场的监视器配合使用:这实际上会导致死锁。

如何保留停车功能? (无法删除 ParkingQuery 上的同步,因为如果查询不同步并且坏事开始发生,SQLite 就会爆炸)。

请注意,必须同时访问 SQLLite,因为这是一个学校项目。

编辑: 期望的停车行为: 如果用户想借一辆车但它不可用,则用户必须等待其他人从借出的车辆归还。

【问题讨论】:

  • 如果 Parking 是唯一使用 ParkingAPI 更新数据库的类,我会说你不需要同步两者。 SQLite 查询位于同步块内,因此它是线程安全的。我不明白为什么 synchronized 关键字对于停车来说是不够的。我不喜欢你的名字。停车API?我希望 ParkingDAO 或 ParkingRepository。
  • 除了来自域范围的命名外,如果 2 个用户需要从同一个停车场借出并且只有 1 辆车可用,则想要的行为是一个用户将等待车辆可用,而没有条件变量都将看到车辆数量 == 1 并继续进行借贷查询,这可能很难预测副作用。
  • 当然它不是唯一的课程,还有其他的东西,比如注册、登录、添加新的停车场和车辆,除了使用条件变量贷款之外,一切都可以正常工作。
  • 听起来 SQLite 是必需的。太糟糕了——我认为你不会在 MySQL 或 PostgreSQL 上遇到那么多问题。可能值得尝试换出数据库,看看情况是否有所改善。
  • 如果您使用 BlockingDeque 和生产者/消费者,那么您的问题将很容易解决,没有这些废话:javarevisited.blogspot.com/2012/02/…

标签: java sqlite concurrency synchronized


【解决方案1】:

这段代码:

long lendVehicle(){
        lock.lock();
        try{
          ...
    }

错了。你基本上锁定然后它不能保证解锁,因为它在try 块之外。尝试更恰当地分解问题。你有一个生产者,它是汽车的回馈,然后你的消费者借给汽车。因此,您知道您的“车位”只能容纳 N 辆汽车(例如清酒,可以说 10 辆)。因此,如果您的缓冲区(停车场)已满,您不想让更多的汽车归还(考虑尝试将车停在没有停车位的车库中)。

现在您可以简单地检查您的返回车功能,如果缓冲区已满,则忽略该操作。借车时要确保车位不空。

【讨论】:

  • 也许问题是停车场有无限车限制?
  • 这不是一个有效的理由。求解小 N 然后泛化
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-25
  • 2016-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多