防止并发访问servlet中的方法

时间:2018-12-27 23:38:02

标签: java multithreading servlets concurrency

我在Servlet中有一种方法,可以在数据库中插入补习预订。此方法具有一个业务规则,该规则检查该会话的导师在该日期和时间中是否已经忙碌。代码看起来像这样:

class BookingService {
    public void insert(Booking t) {
        if(available(t.getTutor(), t.getDate(), t.getTime())) {
            bookingDao.insert(t);
        } else {
            // reject
        }
    }
}

问题在于,多个用户可能会同时尝试在同一日期和时间预订同一位导师,并且没有任何阻止他们都通过考试并插入其预订的内容。我尝试使insert()同步并使用锁,但是它不起作用。如何防止同时访问此方法?

2 个答案:

答案 0 :(得分:1)

使用同步是尝试解决此问题的不足方法:

首先,您将对应用程序进行编码,以便一次只能部署一个实例。这不仅仅是关于在云中扩展。对于IT部门来说,通常要举起一个应用程序的多个实例,这样就不会出现单点故障(以便在托管一个实例的机器崩溃的情况下,该应用程序仍然可用)。使用静态同步意味着该锁不会扩展到一个应用程序类加载器之外,因此多个实例仍然可以以容易出错的方式交错工作。

如果您应在某个时候离开项目,以后的维护者可能不会意识到此问题,并可能尝试以您不希望的方式部署应用程序。使用同步意味着您将离开地雷,让他们偶然发现。

第二,使用同步块阻碍了应用程序的并发性,因为一次只能执行一个线程。

因此,您引入了瓶颈,同时又部署了第二个实例,从而切断了运营部门解决瓶颈的能力。不是一个好的解决方案。

由于发布的代码没有显示交易的迹象,我想每个DAO都会创建自己的交易,或者您是在自动提交模式下进行连接。数据库提供事务来帮助解决此问题,并且由于该功能是在数据库中实现的,因此无论运行多少应用程序实例,该功能都将起作用。

解决此问题的一种简单方法是避免将上述缺点排除在外,这是将事务置于服务层,以便所有DAO调用都将在同一事务内执行。您可以让服务层从池中检索数据库连接,启动事务,将连接传递给每个DAO方法调用,提交事务,然后将连接返回到池中。

答案 1 :(得分:0)

解决问题的一种方法是使用synchronized块。您可以选择许多东西作为锁定对象-暂时this应该可以:

class BookingService {
    public void insert(Booking t) {
        synchronized(this) {
            if(available(t.getTutor(), t.getDate(), t.getTime())) {
                bookingDao.insert(t);
            } else {
                // reject
            }
        }
    }
}

如果您有多个servlet实例,则应使用静态对象作为锁。