Java并发-Web应用程序
我想我在我的web应用程序中发现了更多的bug。通常,我不担心并发性问题,但当您遇到ConcurrentModificationException时,您就开始重新考虑您的设计 我将JBossSeam与Jetty上的Hibernate和EHCache结合使用。现在,它是一个具有多个核心的单一应用服务器 我简要地查看了我的代码,发现了一些还没有抛出异常的地方,但我确信它们可以 我使用的第一个servlet过滤器基本上检查是否有消息通知用户后台发生的事件(来自作业或其他用户)。过滤器只是在模式弹出窗口中将消息添加到页面中。消息存储在会话上下文中,因此另一个请求可能会从会话上下文中提取相同的消息 现在,它工作得很好,但是我没有碰到一个有很多并发请求的页面。我想我可能需要编写一些JMeter测试来确保这不会发生 第二个servlet过滤器记录所有传入请求以及会话。这使我能够知道客户端来自何处,运行的浏览器是什么,等等。我最近看到的问题是在图像库页面上(几乎同时有许多请求),我最终得到了并发修改异常,因为我正在向会话添加请求 会话包含一个请求列表,该列表似乎被多个线程命中Java并发-Web应用程序,java,concurrency,seam,Java,Concurrency,Seam,我想我在我的web应用程序中发现了更多的bug。通常,我不担心并发性问题,但当您遇到ConcurrentModificationException时,您就开始重新考虑您的设计 我将JBossSeam与Jetty上的Hibernate和EHCache结合使用。现在,它是一个具有多个核心的单一应用服务器 我简要地查看了我的代码,发现了一些还没有抛出异常的地方,但我确信它们可以 我使用的第一个servlet过滤器基本上检查是否有消息通知用户后台发生的事件(来自作业或其他用户)。过滤器只是在模式弹出窗口
@Entity
public class HttpSession
{
protected List<HttpRequest> httpRequests;
@Fetch(FetchMode.SUBSELECT)
@OneToMany(mappedBy = "httpSession")
public List<HttpRequest> getHttpRequests()
{return(httpRequests);}
...
}
@Entity
public class HttpRequest
{
protected HttpSession httpSession;
@ManyToOne(optional = false)
@JoinColumn(nullable = false)
public HttpSession getHttpSession()
{return(httpSession);}
...
}
出错的部分是当我进行一些比较以查看请求之间的更改时:
for(HttpRequest httpRequest:httpSession.getHttpRequests())
那一行出现了一个并发的修改异常
要解决的问题:
1.JMeter测试在这里有用吗?
2.对于编写可在并发负载下扩展的web应用程序,您推荐哪些书籍?
3.我试着把synchronized放在我认为需要的地方,即放在循环处理请求的方法上,但仍然失败。我还需要做什么
我补充了一些意见:
我曾考虑将记录http请求作为一项后台任务。我可以很容易地生成一个后台任务来保存这些信息。我在试着回忆为什么我没有对它进行太多的评估。我想有一些信息我想在现场得到
如果我让它异步,这将大大提高吞吐量——我必须使用JMeter来测量这些差异
我仍然需要在那里处理并发性问题
谢谢
Walter这是由于列表已被另一个请求修改,而您仍在一个请求中对其进行迭代。将
列表
替换为(单击链接查看javadoc)应该可以解决特定的问题
关于你的其他问题:
1:JMeter测试在这里有用吗
是的,对webapplications进行压力测试和发现并发性错误当然是有用的
2:对于编写可在并发负载下扩展的web应用程序,您推荐哪些书籍
这本书不是专门针对webapplications的,而是针对并发性的,它是该领域最受推荐的书。您也可以将学到的知识完美地应用于Web应用程序,它们是“大量并发”应用程序的完美现实示例
3:我试着把synchronized放在我认为需要的地方,即放在循环请求的方法上,但还是失败了。我还需要做什么
基本上,您需要在同一个锁上同步对列表的任何访问。但是仅仅用ConcurrentLinkedQueue
替换就更容易了
当对任何集合进行迭代时修改该集合时,将发生ConcurrentModificationException。您可以在单个线程中执行此操作,例如:
for( Object o : someList ) {
someList.add( new Object() );
}
使用列表包装列表或返回列表的不可修改副本。迭代器出现异常,因为在迭代过程中,另一个线程正在更改支持迭代器的集合
- 您可以将对列表的访问包装为同步访问(包括添加和迭代),但这有问题,因为遍历列表以及随之进行的处理可能需要更长的时间,并且您将一直保持对列表的锁定
- 另一个选项是复制列表并将副本分发给迭代,如果对象很小,这可能是一个更好的主意,因为您只需在创建副本时持有锁,而不是在迭代列表时持有锁
- 将值存储在ConcurrentHashMap中,该映射使用锁条带化来最小化锁争用。然后,您可以让get方法返回所需键的复制列表,而不是完整的对象,并直接从映射一次访问一个键
正如在这里的另一个答案中提到的,Java并发实践是一本好书 其他海报正确地指出您需要写入线程安全数据结构。这样做,可能会由于线程争用而降低响应时间。由于这本质上是一个日志操作,它是请求本身的副作用(或者我没有正确地理解您吗?),因此您可以生成一个负责写入线程安全数据结构的新线程。这允许您继续执行实际响应,而不是在日志记录操作中消耗响应时间。可能值得研究设置线程池以减少
for( Object o : someList ) {
someList.add( new Object() );
}