假设我有一个名为doSomething()
的方法,我想在多线程应用程序中使用此方法(每个servlet都继承自HttpServlet)。我想知道是否有可能出现竞争条件以下情况:
doSomething()
不是staic方法,它会将值写入数据库。doSomething()
是静态方法但不将值写入数据库。我注意到我的应用程序中的许多方法可能会导致竞争条件或脏读/写。例如,我有一个轮询系统,对于每个投票操作,某个方法将更改该轮询的单个单元格值,如下所示:
[poll_id | poll_data ]
[1 | {choice_1 : 10, choice_2 : 20}]
JSP / Servlets应用程序会自行解决这些问题吗,或者我必须自己解决所有问题?
谢谢..
答案 0 :(得分:4)
这取决于doSomething()
的实施方式以及实际的实施方式。我假设写入数据库使用JDBC连接,不线程安全。这样做的首选方法是创建ThreadLocal
JDBC连接。
至于第二种情况,它取决于方法中发生的事情。如果它不访问任何共享的,可变的状态,那么就没有问题。如果是这样,您可能需要适当地锁定,这可能涉及向每个其他访问这些变量的锁添加锁。
(请注意,仅将这些方法标记为synchronized
不会修复任何并发错误。如果doSomething()
增加了共享对象上的值,则对该变量的所有访问都需要{{1因为synchronized
不是原子操作。如果它像递增计数器一样简单,你可以使用i++
。)
答案 1 :(得分:3)
Servlet API当然不会让并发成为非问题。
写入数据库时,它取决于持久层中的并发策略。悲观锁定,乐观锁定,最后胜利?当你“写入数据库”时,你需要决定如何处理,还有更多的事情要做。当两个人同时点击按钮时,你想要发生了什么?
将doSomething设为静态似乎对此问题没有太大影响。正在发生的事情是相关部分。它是修改静态变量吗?然后是的,可能会有竞争条件。
答案 2 :(得分:2)
servlet api不会为你做任何事情让你的并发问题消失。在servlet上使用synchronized关键字这样的事情是个坏主意,因为你基本上强迫你的线程一次处理一个,这会破坏你对多个用户快速响应的能力。
如果使用Spring或EJB3,则任何一个都将提供线程本地数据库连接以及指定事务的能力。你一定要看看其中一个。
答案 3 :(得分:2)
案例1,您的servlet使用一些访问数据库的代码。数据库具有您应该利用的锁定机制。这有两个重要原因:数据库本身可能会从其他读取和写入数据的应用程序中使用,但这还不足以让您的应用程序处理与自身竞争的问题。并且:您自己的应用程序可以部署到一个缩放的集群Web容器中,其中代码的多个副本在不同的计算机上执行。
因此,有许多标准模式可以处理数据库中的锁,您可能需要阅读悲观和乐观锁定。
servlet API和JBC连接池为您提供了一些有用的保证,这样您就可以在不使用Java同步的情况下编写servlet代码,前提是您的变量在方法范围内,在概念中
Start transaction (perhaps implicit, perhaps on entry to an ejb)
Get connection to DB ( Gets you a connection from pool, associated with your tran)
read/write/update code
Close connection (actually keeps it for your thread until your transaction commits)
Commit (again maybe implictly)
因此,您唯一真正的问题是处理数据库中的任何争用。以上所有这些都倾向于使用诸如JPA之类的东西更好地完成,但是在或多或少的情况下正在发生的事情。
案例2:静态方法,这可能意味着您现在将所有内容都保存在内存结构中。这(禁止某种远程调用)会影响单个JVM并管理您自己的锁定。如果您的JVM或机器崩溃,我猜您会丢失数据。如果你关心你的数据,那么使用数据库可能会更好。
或者,如何完全采用其他方法:servlet通过将消息写入持久性JMS队列来简单地记录“投票”。让一些其他进程从队列中获取投票并添加它们。您不会以这种方式立即向选民提供反馈,但您可以将用户的体验与实际(在类似情况下)非常复杂的处理分开。
答案 4 :(得分:0)
我觉得问题的最佳解决方案是使用“synchronized”关键字和wait / notify这样的东西!