版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、Java 理論與實(shí)踐: 有狀態(tài) Web 應(yīng)用程序都有漏洞嗎?級(jí)別: 中級(jí)Brian Goetz, 高級(jí)主管工程師, Sun Microsystems2008 年 10 月 13 日Servlets 框架 HttpSession 提供的會(huì)話狀態(tài)管理機(jī)制簡(jiǎn)化了有狀態(tài)應(yīng)用程序的創(chuàng)建,但也很容易導(dǎo)致誤用。在沒(méi)有足夠協(xié)作的情況下,許多 Web 應(yīng)用程序?qū)勺償?shù)據(jù)(比如 JavaBeans 類)使用了 HttpSession 這個(gè)機(jī)制,從而使自身面臨大量潛在的并發(fā)性危險(xiǎn)。雖然 Java 生態(tài)系統(tǒng)中存在許多 Web 框架,但它們都直接或間接地基于 Servlets 基礎(chǔ)設(shè)施。Servlets API 提供大
2、量有用特性,包括通過(guò) HttpSession 和 ServletContext 機(jī)制提供的狀態(tài)管理,它允許應(yīng)用程序在跨多個(gè)用戶請(qǐng)求時(shí)保持狀態(tài)。然而,在 Web 應(yīng)用程序中使用共享狀態(tài)受一些微妙的(并且大部分沒(méi)有進(jìn)行說(shuō)明)的規(guī)則控制著,因此導(dǎo)致許多應(yīng)用程序無(wú)意中觸犯了規(guī)則。結(jié)果是許多有狀態(tài) Web 應(yīng)用程序存在難以發(fā)覺(jué)的嚴(yán)重缺陷。 范圍容器在 Servlet 規(guī)范中,ServletContext、HttpSession 和 HttpRequest 對(duì)象被稱為 范圍容器(Scoped container)。它們都有 getAttribute() 和 setAttribute() 方法,為應(yīng)用程序存
3、儲(chǔ)數(shù)據(jù)。這些范圍容器的區(qū)別在于它們的生命周期不同。對(duì)于 HttpRequest,數(shù)據(jù)只在請(qǐng)求期間有效;對(duì)于 HttpSession,數(shù)據(jù)在用戶和應(yīng)用程序的會(huì)話期間有效;而對(duì)于 ServletContext,數(shù)據(jù)在應(yīng)用程序的整個(gè)生命周期中都有效。 由于 HTTP 協(xié)議是沒(méi)有狀態(tài)的,所以范圍容器在構(gòu)造有狀態(tài) Web 應(yīng)用程序時(shí)十分有用;servlet 容器負(fù)責(zé)管理應(yīng)用程序的狀態(tài)和數(shù)據(jù)的生命周期。盡管這個(gè)規(guī)范沒(méi)有強(qiáng)調(diào),但需要保證嵌套在會(huì)話或應(yīng)用程序中的容器在某種程度上是線程安全的,因?yàn)?getAttribute() 和 setAttribute() 方法隨時(shí)都可能被不同的線程調(diào)用(這個(gè)規(guī)范沒(méi)有直接要
4、求這些實(shí)現(xiàn)必須是線程安全的,但它們提供的服務(wù)實(shí)際上提出了這一點(diǎn))。范圍容器還為 Web 應(yīng)用程序提供一個(gè)潛在的好處:容器可以透明地管理應(yīng)用程序狀態(tài)的復(fù)制和故障轉(zhuǎn)移。 會(huì)話session 是特定用戶和 Web 應(yīng)用程序之間的一系列請(qǐng)求-響應(yīng)交換。用戶希望 Web 站點(diǎn)記住他的身份驗(yàn)證憑證、購(gòu)物車(chē)的物品,以及在前面的請(qǐng)求中輸入到 Web 表單的信息。但核心 HTTP 協(xié)議是沒(méi)有狀態(tài)的,這意味著必須將所有請(qǐng)求信息存儲(chǔ)到請(qǐng)求本身。因此,如果要?jiǎng)?chuàng)建比一個(gè)請(qǐng)求-響應(yīng)周期更長(zhǎng)的有用交互,就必須存儲(chǔ)會(huì)話狀態(tài)。servlet 框架允許將每個(gè)請(qǐng)求與一個(gè)會(huì)話關(guān)聯(lián)起來(lái),并提供充當(dāng)值存儲(chǔ)庫(kù)的 HttpSession 接
5、口,用于存儲(chǔ)與該會(huì)話相關(guān)的(鍵,值)數(shù)據(jù)項(xiàng)。清單 1 是一段典型的 servlet 代碼,它用于在 HttpSession 中存儲(chǔ)購(gòu)物車(chē)數(shù)據(jù):清單 1. 使用 HttpSession 存儲(chǔ)購(gòu)物車(chē)數(shù)據(jù)HttpSession session = request.getSession(true);ShoppingCart cart = (ShoppingCart)session.getAttribute(shoppingCart);if (cart = null) cart = new ShoppingCart(.); session.setAttribute(shoppingCart); doSo
6、methingWith(cart);清單 1 提供的是 servlet 的典型用途;這個(gè)應(yīng)用程序查看是否已將一個(gè)對(duì)象放到會(huì)話中,如果還沒(méi)有,它將創(chuàng)建一個(gè)該會(huì)話的后續(xù)請(qǐng)求可以使用的對(duì)象。構(gòu)建在 servlet 之上的 Web 框架(比如 JSP、JSF 和 SpringMVC 等)隱藏了細(xì)節(jié)情況,但對(duì)于標(biāo)記為嵌入到會(huì)話中的數(shù)據(jù),它們替您執(zhí)行了實(shí)際操作。不幸的是,清單 1 中的用法很可能是不正確的。 與線程相關(guān)的問(wèn)題當(dāng) HTTP 請(qǐng)求到達(dá) servlet 容器之后,會(huì)在 servlet 容器管理的線程上下文中創(chuàng)建 HttpRequest 和 HttpResponse 對(duì)象,并傳遞給 servlet
7、 的 service() 方法。servlet 負(fù)責(zé)生成響應(yīng);在響應(yīng)完成之前 servlet 一直控制這個(gè)線程,響應(yīng)完成時(shí)該線程返回到可用的線程池。Servlet 容器沒(méi)有保持線程與會(huì)話之間的聯(lián)系;某個(gè)會(huì)話的下一個(gè)請(qǐng)求很可能由另一個(gè)不同的線程來(lái)處理。事實(shí)上,可能有多個(gè)請(qǐng)求同時(shí)進(jìn)入同一個(gè)會(huì)話(當(dāng)用戶與頁(yè)面交互時(shí),使用框架或 AJAX 技術(shù)從服務(wù)器獲取數(shù)據(jù)的 Web 應(yīng)用程序可能發(fā)生這種現(xiàn)象)。在這種情況,同一用戶可能發(fā)出多個(gè)請(qǐng)求,這些請(qǐng)求在不同的線程上并行執(zhí)行。 大多數(shù)情況下,這類線程問(wèn)題與 Web 應(yīng)用程序開(kāi)發(fā)人員無(wú)關(guān)。由于自身沒(méi)有狀態(tài),HTTP 鼓勵(lì)只有存儲(chǔ)在請(qǐng)求中的數(shù)據(jù)(不與其他并發(fā)請(qǐng)求共
8、享)和存儲(chǔ)在存儲(chǔ)庫(kù)(比如數(shù)據(jù)庫(kù))中的、已進(jìn)行并發(fā)性控制的數(shù)據(jù),才具有響應(yīng)功能。然而,一旦 Web 應(yīng)用程序?qū)?shù)據(jù)存儲(chǔ)在 HttpSession 或 ServletContext 等共享容器之后,該 Web 應(yīng)用程序就具有了并發(fā)性,因此必須考慮應(yīng)用程序內(nèi)部的線程安全問(wèn)題。 盡管我們常用線程安全描述代碼,但實(shí)際上它是描述數(shù)據(jù)的。具體來(lái)說(shuō),線程安全是指適當(dāng)?shù)貐f(xié)調(diào)對(duì)被多個(gè)線程訪問(wèn)的可變數(shù)據(jù)的訪問(wèn)。Servlet 應(yīng)用程序通常是線程安全的,因?yàn)樗鼈儧](méi)有共享任何可變數(shù)據(jù),因此就不需要額外的同步。但可以通過(guò)很多種辦法將共享狀態(tài)引入到 Web 應(yīng)用程序 除了 HttpSession 和 ServletCont
9、ext 等范圍容器外,還可以使用 HttpServlet 對(duì)象的靜態(tài)字段和實(shí)例字段。如果要讓 Web 應(yīng)用程序跨請(qǐng)求共享數(shù)據(jù),開(kāi)發(fā)人員就必須注意共享數(shù)據(jù)的位置,并保證訪問(wèn)共享數(shù)據(jù)時(shí)線程之間有足夠的協(xié)作(同步),以避免與線程相關(guān)的危險(xiǎn)。 Web 應(yīng)用程序的線程風(fēng)險(xiǎn)如果 Web 應(yīng)用程序?qū)①?gòu)物車(chē)等可變會(huì)話數(shù)據(jù)存儲(chǔ)在 HttpSession 中,就有可能出現(xiàn)兩個(gè)請(qǐng)求試圖同時(shí)訪問(wèn)該購(gòu)物車(chē)。這可能導(dǎo)致以下幾種故障: 原子性故障。在數(shù)據(jù)不一致的狀態(tài)下,一個(gè)線程正在更新多個(gè)數(shù)據(jù)項(xiàng),而另一個(gè)線程正在讀取這些數(shù)據(jù) 讀線程和寫(xiě)線程之間的可見(jiàn)性故障。一個(gè)線程修改購(gòu)物車(chē),但另一個(gè)線程看到的購(gòu)物車(chē)內(nèi)容的狀態(tài)是過(guò)時(shí)的或不
10、一致的 原子性故障清單 2 展示了一個(gè)用于在游戲應(yīng)用程序中設(shè)置和獲取最高分的不恰當(dāng)?shù)姆椒▽?shí)現(xiàn)。它使用一個(gè) PlayerScore 對(duì)象來(lái)表示最高分,這實(shí)際上是一個(gè)具有 name 和 score 屬性的普通 JavaBean 類,存儲(chǔ)在內(nèi)嵌于應(yīng)用程序的 ServletContext 中(假設(shè)在應(yīng)用程序啟動(dòng)時(shí),初始最高分在 ServletContext 中被設(shè)置為 highScore 屬性,因此 getAttribute() 調(diào)用不會(huì)失敗)。 清單 2. 在范圍容器中存儲(chǔ)相關(guān)項(xiàng)的不恰當(dāng)模式public PlayerScore getHighScore() ServletContext ctx =
11、getServletConfig().getServletContext(); PlayerScore hs = (PlayerScore) ctx.getAttribute(highScore); PlayerScore result = new PlayerScore(); result.setName(hs.getName(); result.setScore(hs.getScore(); return result;public void updateHighScore(PlayerScore newScore) ServletContext ctx = getServletConfi
12、g().getServletContext(); PlayerScore hs = (PlayerScore) ctx.getAttribute(highScore); if (newScore.getScore() hs.getScore() hs.setName(newScore.getName(); hs.setScore(newScore.getScore(); 清單 2 中的代碼不夠好。這里采用的方法是在 ServletContext 中存儲(chǔ)一個(gè)包含最高分玩家的名字和分?jǐn)?shù)的可變?nèi)萜?。?dāng)打破記錄時(shí),必須更新名字和分?jǐn)?shù)。 假如當(dāng)前的最高分玩家是 Bob,他的分?jǐn)?shù)為 1000,但是 Joe
13、 以 1100 分打破了這個(gè)記錄。在正要設(shè)置 Joe 的分?jǐn)?shù)時(shí),另一個(gè)玩家又發(fā)出獲得最高分的請(qǐng)求。getHighScore() 將從 servlet 上下文獲取 PlayerScore 對(duì)象,然后從該對(duì)象獲取名字和分?jǐn)?shù)。如果不慎出現(xiàn)計(jì)時(shí)失誤,就有可能獲取 Bob 的名字和 Joe 的分?jǐn)?shù),顯示 Bob 取得了 1100 分,而實(shí)際上 Bob 并沒(méi)有獲得這個(gè)分?jǐn)?shù)(這種故障在免費(fèi)游戲站點(diǎn)上是可以接受的,因?yàn)?“分?jǐn)?shù)” 并不是 “銀行存款”)。這是一種原子性故障,因?yàn)楸舜酥g本應(yīng)該是原子關(guān)系的兩個(gè)操作 獲取名字/分?jǐn)?shù)對(duì)和更新名字/分?jǐn)?shù)對(duì) 實(shí)際上并沒(méi)有按照原子關(guān)系執(zhí)行,而且其中一個(gè)線程可以在不一致?tīng)顟B(tài)
14、下查看共享數(shù)據(jù)。另外,由于分?jǐn)?shù)更新邏輯遵循 check-then-act 模式,因此可能出現(xiàn)兩個(gè)線程 “爭(zhēng)奪” 更新最高分,從而導(dǎo)致難以預(yù)料的結(jié)果。假設(shè)當(dāng)前的最高分是 1000,有兩個(gè)玩家同時(shí)注冊(cè)更高的分?jǐn)?shù) 1100 和 1200。如果出現(xiàn)計(jì)時(shí)失誤,這兩個(gè)分?jǐn)?shù)都能夠通過(guò) “高于現(xiàn)有分?jǐn)?shù)的最高分” 檢查,并且都進(jìn)入到更新最高分的代碼塊中。和前面一樣,根據(jù)計(jì)時(shí)的實(shí)際情況,最后的結(jié)果可能不一致(采用一個(gè)玩家的名字和另一個(gè)玩家的分?jǐn)?shù)),或者出現(xiàn)錯(cuò)誤(分?jǐn)?shù)為 1100 的玩家可能覆蓋分?jǐn)?shù)為 1200 的玩家)。 可見(jiàn)性故障 比原子性故障更復(fù)雜的是可見(jiàn)性 故障。沒(méi)有同步時(shí),如果一個(gè)線程讀取另外一個(gè)線程正在
15、寫(xiě)的變量,讀的線程將看到過(guò)時(shí)的 數(shù)據(jù)。更糟糕的是,讀線程還可能會(huì)看到 x 變量的最新數(shù)據(jù)和 y 變量的過(guò)時(shí)數(shù)據(jù),即使先寫(xiě) y 變量也是這樣??梢?jiàn)性故障非常復(fù)雜,因?yàn)樗陌l(fā)生是隨機(jī)的,甚至是頻繁的,這會(huì)導(dǎo)致罕見(jiàn)的難以調(diào)試的間發(fā)性故障??梢?jiàn)性故障是由數(shù)據(jù)爭(zhēng)奪引起的 訪問(wèn)共享變量時(shí)不能正確同步。爭(zhēng)奪數(shù)據(jù)的程序,不管它是什么樣的,都屬于有漏洞的程序,因?yàn)樗鼈兊男袨椴荒芸煽款A(yù)測(cè)。 Java Memory Model(JMM)定義一些條件,它們保證讀變量的線程能夠看到另一個(gè)線程的寫(xiě)入結(jié)果(詳細(xì)講解 JMM 超出了本文的范圍;參見(jiàn) 參考資料)。JMM 在一個(gè)稱為 happens-before 的程序的操作上
16、定義一個(gè)排序。只有在通用鎖上執(zhí)行同步或訪問(wèn)一個(gè)通用的可變變量時(shí),才能創(chuàng)建跨線程的 Happens-before 排序。在沒(méi)有 happens-before 排序的情況下, Java 平臺(tái)可以延遲或更改順序,按照這個(gè)順序,一個(gè)線程的寫(xiě)操作對(duì)于另一個(gè)讀取同一變量的線程是可見(jiàn)的。 清單 2 中的代碼不僅有原子性故障,還有可見(jiàn)性故障。updateHighScore() 方法從 ServletContext 獲取 HighScore 對(duì)象,然后修改它的狀態(tài)。這樣做的目的是讓其他調(diào)用 getHighScore() 的線程看見(jiàn)這些修改,但是如果 updateHighScore() 的 name 和 scor
17、e 屬性的寫(xiě)操作和其他調(diào)用 getHighScore() 的線程的 name 和 score 屬性的讀操作之間沒(méi)有 happens-before 排序,我們只能期盼運(yùn)氣好些,讓讀線程能夠看到正確的值。 可能的解決方案盡管 servlet 規(guī)范沒(méi)有充分地描述 servlet 容器必須提供的 happens-before 保證,但可以得出結(jié)論:將一個(gè)屬性放置在共享范圍容器(HttpSession 或 ServletContext)應(yīng)該在另一個(gè)線程獲取該屬性之前發(fā)生。(參見(jiàn) JCi 了解這個(gè)結(jié)論的推理過(guò)程。該規(guī)范中這樣描述:“執(zhí)行請(qǐng)求線程的多個(gè) servlets 可能同時(shí)積極地訪問(wèn)單個(gè)會(huì)話對(duì)象。開(kāi)發(fā)
18、人員負(fù)責(zé)恰當(dāng)?shù)赝綄?duì)會(huì)話資源的訪問(wèn))。 set-after-write 技巧更新存儲(chǔ)在其他會(huì)話容器中的可變數(shù)據(jù)時(shí),必須在修改該數(shù)據(jù)后再次調(diào)用 setAttribute()。這是一種常用的最佳實(shí)踐。清單 3 展示了重寫(xiě) updateHighScore() 以使用這個(gè)技巧的示例(這個(gè)技巧的目的之一是提示容器值已經(jīng)更改,因此可以在分布式 Web 應(yīng)用程序的各個(gè)實(shí)例之間重新同步會(huì)話和應(yīng)用程序狀態(tài))。 清單 3. 使用 set-after-write 技巧提示 servlet 容器值已經(jīng)更新public void updateHighScore(PlayerScore newScore) Servlet
19、Context ctx = getServletConfig().getServletContext(); PlayerScore hs = (PlayerScore) ctx.getAttribute(highScore); if (newScore.getScore() hs.getScore() hs.setName(newScore.getName(); hs.setScore(newScore.getScore(); ctx.setAttribute(highScore, hs); 不幸的是,盡管這個(gè)技巧能夠在集群應(yīng)用程序中高效地復(fù)制會(huì)話和應(yīng)用程序狀態(tài),但它不能解決本例中的基本線程安
20、全問(wèn)題。它可以減輕可見(jiàn)性問(wèn)題(即另一個(gè)玩家可能永遠(yuǎn)看不到在 updateHighScore() 中更新的值),但還不能解決許多潛在的原子性問(wèn)題。 利用同步set-after-write 技巧可以消除可見(jiàn)性問(wèn)題,因?yàn)?happens-before 排序是可傳遞的,因而調(diào)用 updateHighScore() 中的 setAttribute() 和調(diào)用 getHighScore() 中的 getAttribute() 之間有一個(gè)邊緣地帶。因?yàn)?HighScore 狀態(tài)的更新在 setAttribute() 之前發(fā)生,setAttribute() 狀態(tài)的更新在從 getAttribute() 返回之
21、前發(fā)生,getAttribute() 狀態(tài)的更新在 getHighScore() 的調(diào)用方使用狀態(tài)之前發(fā)生,所以通過(guò)這種傳遞可以得出結(jié)論:調(diào)用方 getHighScore() 看到的值至少和 setAttribute() 的最近一次調(diào)用一樣新。這個(gè)技巧稱為利用同步(piggybacking on synchronization),因?yàn)?getHighScore() 和 updateHighScore() 方法能夠在 getAttribute() 和 setAttribute() 中使用同步信息來(lái)提供一些可見(jiàn)性保證。然而,在上面這個(gè)例子中,這還不能完全解決問(wèn)題。set-after-write 技
22、巧可能對(duì)狀態(tài)復(fù)制非常有用,但還不能提供線程安全。 了解不可修改性要?jiǎng)?chuàng)建線程安全的應(yīng)用程序,一個(gè)有用的技巧便是盡可能多地使用不可修改的數(shù)據(jù)。清單 4 展示了重寫(xiě)后的最高分示例,它使用了 HighScore 的不可修改的 實(shí)現(xiàn),從而避免了原子性故障(允許調(diào)用方看見(jiàn)不存在的玩家/分?jǐn)?shù)對(duì))和可見(jiàn)性故障(阻止 getHighScore() 的調(diào)用方看見(jiàn)在調(diào)用 updateHighScore() 時(shí)寫(xiě)的最新值): 清單 4. 使用不可修改的 HighScore 對(duì)象修復(fù)原子性和可見(jiàn)性漏洞Public class HighScore public final String name; public fina
23、l int score; public HighScore(String name, int score) = name; this.score = score; public PlayerScore getHighScore() ServletContext ctx = getServletConfig().getServletContext(); return (PlayerScore) ctx.getAttribute(highScore);public void updateHighScore(PlayerScore newScore) ServletContext
24、 ctx = getServletConfig().getServletContext(); PlayerScore hs = (PlayerScore) ctx.getAttribute(highScore); if (newScore.score hs.score) ctx.setAttribute(highScore, newScore); 清單 4 中的代碼的潛在故障很少。在 setAttribute() 和 getAttribute() 中使用同步保證了可見(jiàn)性。實(shí)際上,僅存儲(chǔ)單個(gè)不可修改數(shù)據(jù)項(xiàng)消除了潛在的原子性故障,即 getHighScore() 的調(diào)用方可以看見(jiàn)名字/分?jǐn)?shù)對(duì)的不一
25、致更新。 將不可修改對(duì)象放置在范圍容器避免了許多原子性和可見(jiàn)性故障;將有效不可修改性 對(duì)象放置在范圍容器中也是安全的。有效不可修改性對(duì)象是指那些雖然理論上是可修改的,但實(shí)際上在發(fā)布之后再?zèng)]有被更改過(guò)的對(duì)象,比如 JavaBean,將一個(gè)對(duì)象放置到 HttpSession 中之后,它的 setter 方法就不再被調(diào)用。 放置在 HttpSession 中的數(shù)據(jù)不僅被該會(huì)話的請(qǐng)求訪問(wèn);它還可能被容器本身訪問(wèn)(如果容器進(jìn)行狀態(tài)復(fù)制的話)。所有放置在 HttpSession 或 ServletContext 中的數(shù)據(jù)應(yīng)該是線程安全的或有效不可修改的。 影響原子狀態(tài)轉(zhuǎn)換但是 清單 4 中的代碼仍然有一個(gè)
26、問(wèn)題 updateHighScore() 中的 check-then-act 仍然使兩個(gè)試圖更新最高分?jǐn)?shù)的線程之間存在潛在 “爭(zhēng)奪”。如果計(jì)時(shí)失誤,有一個(gè)更新可能會(huì)丟失。兩個(gè)線程可能同時(shí)通過(guò)了 “高于現(xiàn)有分?jǐn)?shù)的新最高分” 檢查,造成它們同時(shí)調(diào)用 setAttribute()。不能確保兩個(gè)分?jǐn)?shù)中最高者獲得調(diào)用,這取決于計(jì)時(shí)。要修復(fù)這個(gè)最后的漏洞,我們需要一種原子性地更新分?jǐn)?shù)引用的方法,同時(shí)又要保證不受干擾。有幾種方法可以實(shí)現(xiàn)這個(gè)目的。 清單 5 為 updateHighScore() 添加了同步,確保更新進(jìn)程中固有的 check-then-act 不和另一個(gè)更新并發(fā)執(zhí)行。如果所有條件修改邏輯獲得
27、 updateHighScore() 使用的同一個(gè)鎖,用這種方法就可以了。 清單 5. 使用同步修復(fù)最后一個(gè)原子性漏洞public void updateHighScore(PlayerScore newScore) ServletContext ctx = getServletConfig().getServletContext(); PlayerScore hs = (PlayerScore) ctx.getAttribute(highScore); synchronized (lock) if (newScore.score hs.score) ctx.setAttribute(high
28、Score, newScore); 雖然清單 5 中的技術(shù)是可行的,但還有一個(gè)更好的技術(shù):使用 包中的 AtomicReference 類。這個(gè)類的用途就是通過(guò) compareAndSet() 調(diào)用提供原子條件更新。清單 6 展示了如何使用 AtomicReference 來(lái)修復(fù)本示例的最后一個(gè)原子性問(wèn)題。這個(gè)方法比清單 5 中的代碼好,因?yàn)楹茈y違背更新最高分?jǐn)?shù)的規(guī)則。 清單 6. 使用 AtomicReference 來(lái)修復(fù)最后一個(gè)原子性漏洞public PlayerScore getHighScore() ServletContext ctx = getServletConfig().ge
29、tServletContext(); AtomicReference holder = (AtomicReference) ctx.getAttribute(highScore); return holder.get();public void updateHighScore(PlayerScore newScore) ServletContext ctx = getServletConfig().getServletContext(); AtomicReference holder = (AtomicReference) ctx.getAttribute(highScore); while
30、(true) HighScore old = holder.get(); if (old.score = newScore.score) break; else if (pareAndSet(old, newScore) break; 對(duì)于放置在范圍容器中的可修改數(shù)據(jù),應(yīng)該將它們的狀態(tài)轉(zhuǎn)換變成原子性的,這可以通過(guò)同步或 中的原子變量類來(lái)實(shí)現(xiàn)。 序列化對(duì) HttpSession 的訪問(wèn)在我已給出的示例中,我試圖避免與訪問(wèn)整個(gè)應(yīng)用程序中的 ServletContext 相關(guān)的各種危險(xiǎn)。很明顯,訪問(wèn) ServletContext 時(shí)需要細(xì)心的協(xié)作,因?yàn)槿魏握?qǐng)求都可以訪問(wèn) ServletContext
31、。然而,大多數(shù)有狀態(tài) Web 應(yīng)用程序嚴(yán)重依賴于內(nèi)嵌于會(huì)話的容器 HttpSession。同一個(gè)會(huì)話中發(fā)生多個(gè)同步請(qǐng)求的原因不是很直觀;畢竟,每個(gè)會(huì)話都是綁定到一個(gè)特定用戶或?yàn)g覽器會(huì)話的,并且用戶不一定一次請(qǐng)求多個(gè)頁(yè)面。但是在編程式地生成請(qǐng)求的應(yīng)用程序中(比如 AJAX 應(yīng)用程序),一個(gè)會(huì)話中的請(qǐng)求是可以重疊的。 單個(gè)會(huì)話中的請(qǐng)求當(dāng)然可以是重疊的,但這不是一件好事。如果可以輕松地序列化會(huì)話中的請(qǐng)求,當(dāng)訪問(wèn) HttpSession 中的共享對(duì)象時(shí),這里提到的所有問(wèn)題幾乎都可以解決;序列化可以阻止原子性故障,并且利用 HttpSession 中的同步可以阻止可見(jiàn)性故障。序列化特定會(huì)話的請(qǐng)求不會(huì)對(duì)吞
32、吐量造成很大的影響,因?yàn)橐粋€(gè)會(huì)話中的請(qǐng)求很少重疊,在一個(gè)會(huì)話中出現(xiàn)很多請(qǐng)求重疊就更罕見(jiàn)了。 不幸的是,servlet 規(guī)范并沒(méi)有提到 “序列化同一會(huì)話中的請(qǐng)求”。不過(guò) SpringMVC 框架提供了一種方法,并且這種方法可以在其他框架中輕松實(shí)現(xiàn)。SpringMVC 控制器的基類 AbstractController 提供了一個(gè)布爾變量 synchronizeOnSession;設(shè)置這里之后,它將使用一個(gè)鎖,確保一個(gè)會(huì)話中只同時(shí)執(zhí)行一個(gè)請(qǐng)求。 序列化 HttpSession 上的請(qǐng)求消除了很多并發(fā)性危險(xiǎn)。同樣,將對(duì)象限制在 Event Dispatch Thread(EDT)中減少了 Swing 應(yīng)用程序中的同步需求。結(jié)束語(yǔ)許多有狀態(tài) Web 應(yīng)用程序有很?chē)?yán)重的并發(fā)
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 美容院前臺(tái)服務(wù)員工作總結(jié)
- 家居建材行業(yè)營(yíng)銷(xiāo)工作總結(jié)
- 二零二五年度二手車(chē)交易個(gè)人信用貸款合作協(xié)議3篇
- 二零二五版消費(fèi)信貸合同參考范本3篇
- 二零二五版小產(chǎn)權(quán)房屋交易協(xié)議(附裝修及配套設(shè)施承諾)6篇
- 2025版淘寶商家與消費(fèi)者金融支付合同3篇
- 二零二五年度汽車(chē)租賃及維修一體化服務(wù)協(xié)議4篇
- 2025版石榴品牌授權(quán)與市場(chǎng)推廣合作協(xié)議3篇
- 二零二五年度個(gè)人消費(fèi)貸款合同個(gè)人信息保護(hù)條款3篇
- 二零二五年度綠色蔬菜直供社區(qū)配送合同2篇
- 《小兒靜脈輸液速度》課件
- 營(yíng)銷(xiāo)人員薪酬標(biāo)準(zhǔn)及績(jī)效考核辦法
- 香港朗文4B單詞及句子
- 醫(yī)院每日消防巡查記錄表
- 運(yùn)輸企業(yè)重大危險(xiǎn)源辨識(shí)及排查制度
- 運(yùn)動(dòng)技能學(xué)習(xí)與控制課件第五章運(yùn)動(dòng)中的中樞控制
- 中心血站改造項(xiàng)目謀劃建議書(shū)
- 高中數(shù)學(xué)三角函數(shù)圖像變換訓(xùn)練-含答案
- 初中英語(yǔ)專項(xiàng)練習(xí)介詞專項(xiàng)訓(xùn)練
- 財(cái)務(wù)部規(guī)范化管理 流程圖
- GB/T 20631.2-2006電氣用壓敏膠粘帶第2部分:試驗(yàn)方法
評(píng)論
0/150
提交評(píng)論