guava 并发
编写并发Java应用程序时最令人讨厌的问题之一是对线程之间共享的资源的处理,例如Web应用程序的会话和应用程序数据。 结果,如果应用程序的并发级别很低,许多开发人员选择根本不同步这些资源。 例如,不太可能同时访问会话资源:如果请求周期在短时间内完成,则在第一个请求周期仍在进行时,用户不太可能使用第二个浏览器选项卡发送并发请求。 随着Ajax驱动的Web应用程序的兴起,这种信任方法的确变得越来越危险。 在Ajax应用程序中,例如,用户可以在另一个浏览器窗口中启动类似任务时,请求完成一项较长时间的任务。 如果这些任务访问或写入会话数据,则需要同步此类访问。 否则,您将面临细微的错误,甚至会遇到安全问题,例如本博客文章中指出的那样 。
Java的synced关键字是引入锁的一种简单方法。 例如,此示例仅在需要将新实例写入会话时才阻塞请求周期的线程。
HttpSession session = request.getSession(true);
if (session.getAttribute("shoppingCart") == null) {synchronize(session) {if(session.getAttribute("shoppingCart")= null) {cart = new ShoppingCart();session.setAttribute("shoppingCart");}}
}
ShoppingCart cart = (ShoppingCart)session.getAttribute("shoppingCart");
doSomethingWith(cart);
此代码会将新的ShoppingCart实例添加到会话中。 每当找不到购物车时,该代码将获取当前用户会话的监视器,并将新的ShoppingCart添加到当前用户的HttpSession中。 但是,此解决方案具有以下缺点:
- 每当通过与上述相同的方法将任何值添加到会话中时,任何正在访问当前会话的线程都将阻塞。 当两个线程尝试访问不同的会话值时,也会发生这种情况。 这将使应用程序的限制更加严格。
- Servlet API实现可能选择实现HttpSession而不是一个单例实例。 在这种情况下,整个同步将失败。 (但是,这不是Servlet API的常见实现。)
最好找到一个要与HttpSession实例进行同步的对象。 但是,创建此类对象并在不同线程之间共享它们会带来相同的问题。 避免这种情况的一种好方法是使用既固有并发又允许使用弱键的Guava缓存:
LoadingCache<String, Object> monitorCache = CacheBuilder.newBuilder().weakValues().build(new CacheLoader<String, Object>{public Object load(String key) {return new Object();}});
现在,我们可以像这样重写锁定代码:
HttpSession session = request.getSession(true);
Object monitor = ((LoadingCache<String,Object>)session.getAttribute("cache")).get("shoppingCart");
if (session.getAttribute("shoppingCart") == null) {synchronize(monitor) {if(session.getAttribute("shoppingCart")= null) {cart = new ShoppingCart();session.setAttribute("shoppingCart");}}
}
ShoppingCart cart = (ShoppingCart)session.getAttribute("shoppingCart");
doSomethingWith(cart);
Guava缓存是自填充的,将仅返回一个Monitor Object实例,该实例可用作对共享会话资源的锁定,该资源由shoppingCart普遍标识。 Guava缓存由ConcurrentHashMap支持,该ConcurrentHashMap通过仅在映射键的哈希值存储桶上进行同步来避免同步。 结果,使应用程序成为线程安全的,而不会全局阻止它。 另外,您不必担心内存不足,因为如果不再使用监视器(和相关的缓存条目),则会被垃圾回收。 如果您不使用其他缓存,甚至可以考虑使用软引用来优化运行时间。
当然可以完善这种机制。 例如,除了返回Object实例之外,还可以返回ReadWriteLock。 同样,在会话启动时实例化LoadingCache也很重要。 例如,这可以通过HttpSessionListener实现。
翻译自: https://www.javacodegeeks.com/2013/12/object-based-micro-locking-for-concurrent-applications-by-using-guava.html
guava 并发