使用Guava对并发应用程序进行基于对象的微锁定
編寫并發(fā)Java應(yīng)用程序時(shí)最令人討厭的問題之一是對(duì)線程之間共享的資源的處理,例如Web應(yīng)用程序的會(huì)話和應(yīng)用程序數(shù)據(jù)。 結(jié)果,如果應(yīng)用程序的并發(fā)級(jí)別很低,許多開發(fā)人員選擇根本不同步這些資源。 例如,不太可能同時(shí)訪問會(huì)話資源:如果請(qǐng)求周期在很短的時(shí)間內(nèi)完成,那么當(dāng)?shù)谝粋€(gè)請(qǐng)求周期仍在進(jìn)行時(shí),用戶不太可能使用第二個(gè)瀏覽器選項(xiàng)卡發(fā)送并發(fā)請(qǐng)求。 隨著Ajax驅(qū)動(dòng)的Web應(yīng)用程序的興起,這種信任方法的確變得越來越危險(xiǎn)。 在Ajax應(yīng)用程序中,例如,用戶可以在另一個(gè)瀏覽器窗口中啟動(dòng)類似任務(wù)時(shí),請(qǐng)求完成一項(xiàng)較長(zhǎng)時(shí)間的任務(wù)。 如果這些任務(wù)訪問或?qū)懭霑?huì)話數(shù)據(jù),則需要同步此類訪問。 否則,您將面臨細(xì)微的錯(cuò)誤,甚至?xí)龅桨踩珕栴},例如本博客文章中指出的那樣 。
Java的synced關(guān)鍵字是引入鎖的一種簡(jiǎn)單方法。 例如,此示例僅在需要將新實(shí)例寫入會(huì)話時(shí)才阻塞請(qǐng)求周期的線程。
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);此代碼會(huì)將新的ShoppingCart實(shí)例添加到會(huì)話中。 每當(dāng)找不到購(gòu)物車時(shí),該代碼將獲取當(dāng)前用戶會(huì)話的監(jiān)視器,并將新的ShoppingCart添加到當(dāng)前用戶的HttpSession中。 但是,此解決方案具有以下缺點(diǎn):
最好找到要與HttpSession實(shí)例同步的其他對(duì)象。 但是,創(chuàng)建此類對(duì)象并在不同線程之間共享它們會(huì)帶來相同的問題。 避免這種情況的一種好方法是使用既固有并發(fā)又允許使用弱鍵的Guava緩存:
LoadingCache<String, Object> monitorCache = CacheBuilder.newBuilder().weakValues().build(new CacheLoader<String, Object>{public Object load(String key) {return new Object();}});現(xiàn)在我們可以這樣重寫鎖定代碼:
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緩存是自填充的,將僅返回一個(gè)Monitor Object實(shí)例,該實(shí)例可用作對(duì)共享會(huì)話資源的鎖定,該資源由shoppingCart普遍標(biāo)識(shí)。 Guava緩存由ConcurrentHashMap支持,該ConcurrentHashMap通過僅在映射鍵的哈希值存儲(chǔ)桶上進(jìn)行同步來避免同步。 結(jié)果,使應(yīng)用程序成為線程安全的,而不會(huì)全局阻止它。 另外,您不必?fù)?dān)心內(nèi)存用完,因?yàn)槿绻辉偈褂帽O(jiān)視器(和相關(guān)的緩存條目),則會(huì)被垃圾回收。 如果您不使用其他緩存,甚至可以考慮使用軟引用來優(yōu)化運(yùn)行時(shí)間。
當(dāng)然可以完善這種機(jī)制。 例如,可以返回一個(gè)ReadWriteLock而不是返回Object實(shí)例。 同樣,在會(huì)話啟動(dòng)時(shí)實(shí)例化LoadingCache也很重要。 這可以通過例如HttpSessionListener來實(shí)現(xiàn)。
翻譯自: https://www.javacodegeeks.com/2013/12/object-based-micro-locking-for-concurrent-applications-by-using-guava.html
總結(jié)
以上是生活随笔為你收集整理的使用Guava对并发应用程序进行基于对象的微锁定的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大型布线:Java云应用程序缺少的技术
- 下一篇: 镜像镜像–使用反射在运行时查看JVM内部