qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請訪問 http://qaseven.github.io/

          有關(guān)軟件缺陷的知識

           【軟件缺陷的定義】

            首先是Bug的定義:在軟件程序中存在的任何一種破壞正常運行能力的問題或缺陷,都可以叫做“Bug”。

           ?。?)軟件未達到軟件產(chǎn)品需求說明書中的要求

           ?。?)軟件出現(xiàn)了軟件產(chǎn)品需求說明書中指明不會出現(xiàn)的錯誤

            (3)軟件功能超出了軟件產(chǎn)品需求說明書中指明的范圍

           ?。?)軟件未達到軟件產(chǎn)品說明書中未指明但應(yīng)達到的要求

            (5)測試人員認為難以理解、不易使用、運行緩慢或最終用戶認為不好的問題

            【軟件缺陷的級別】

            建議:可用性方面的一些建議,如字體顏色等一些不影響使用的問題。

            提示:一些小問題,如有個別錯別字、文字排版不整齊等,對功能幾乎沒有影響,軟件產(chǎn)品仍可使用。

            一般:不太嚴(yán)重的錯誤,如次要功能模塊喪失、提示信息不夠準(zhǔn)確、用戶界面差和操作時間長等。

            嚴(yán)重:嚴(yán)重錯誤,指功能模塊或特性沒有實現(xiàn),主要功能部分喪失,次要功能全部喪失或致命的錯誤聲明。

            致命:致命的錯誤,造成系統(tǒng)崩潰、死機或造成數(shù)據(jù)丟失、主要功能完全喪失等。

            【軟件缺陷的狀態(tài)】

            凡是使用過缺陷管理工具,如BugFree、JIRA等都會知道Bug無非是這幾種狀態(tài):新建、接受/處理、拒絕、已修復(fù)、關(guān)閉、重新打開、掛起。狀態(tài)之間的跳轉(zhuǎn)圖如下:

            【軟件缺陷的處理】

            上面的知識點在各種網(wǎng)站和書籍上都可以查找到,但實際測試當(dāng)中,測試人員需要嚴(yán)格的按照測試流程執(zhí)行,時時檢查開發(fā)人員是否在未溝通的情況下掛起或掛起B(yǎng)UG,另外軟件發(fā)布時,基本上很少能達到100%的Bug修復(fù)后上線,那么如何在還有Bug遺留的情況下,評估是否可以發(fā)布呢?

            1、缺陷的掛起率

            首先項目發(fā)布時,缺陷的掛起率不能超過15%,并且被掛起的Bug也需要對影響面進行評估,對用戶影響大的,比如有延遲問題,延遲時間超過15s,這類bug都原則上不允許掛起,需要優(yōu)化解決,另外在測試報告中的測試建議中可以說明:

            ● 可以全量發(fā)布:適用于沒有掛起bug或沒有重現(xiàn)率高的嚴(yán)重致命的掛起bug。

            ● 建議灰度發(fā)布:適用于掛起的嚴(yán)重致命bug重現(xiàn)率低(低于50%),或用戶不容易感知。

            ● 不建議發(fā)布:適用于掛起的嚴(yán)重致命bug必現(xiàn),或很干擾用戶體驗。

            2、遺留Bug的影響

            測試人員在報告中要對遺留Bug的影響度進行大致評估,關(guān)注的地方有Bug的重現(xiàn)概率、Bug對用戶造成的影響、Bug是否會引發(fā)其他功能模塊的使用來進行判斷。

          posted @ 2012-05-29 10:07 順其自然EVO 閱讀(604) | 評論 (0)編輯 收藏

          讓產(chǎn)品設(shè)計師跟蹤測試產(chǎn)品

          注:先明確一下這里所說的產(chǎn)品設(shè)計師的職責(zé):需求收集、信息架構(gòu)、交互設(shè)計、產(chǎn)品設(shè)計文檔撰寫。

            “我們的設(shè)計很好,可開發(fā)的產(chǎn)品很差!”,這個問題想必困擾著不少公司或團隊,在近期的工作中,逐漸體會到一套行之有效的方法–讓產(chǎn)品設(shè)計師跟蹤測試自己設(shè)計的產(chǎn)品。設(shè)計師不要小看這測試的工作,跟蹤測試起來頗有成就,你可以知道你的設(shè)計被實施了多少,看著實施符合設(shè)計,設(shè)計師會很有成就感。我也是被CTO逼著走過了這個過程才逐漸體會到它的好處。

            產(chǎn)品設(shè)計師不大可能與程序員一起寫程序,但可以跟蹤測試開發(fā)的產(chǎn)品,并把測試結(jié)果直接反饋給大家(程序員、項目經(jīng)理、產(chǎn)品經(jīng)理、測試人員)。這應(yīng)該算是一個管理問題,確切的說是協(xié)作流程問題。所以這種做法,必須得到cto等高層管理者的大力推行,否則編程者是不買帳的,畢竟誰都不愿意讓人跟在屁股后面指責(zé)哪里做錯了,高管把產(chǎn)品設(shè)計師的測試工作納入流程,大家照章辦事,工作起來會更順利一些。

            測試的時機:

            產(chǎn)品開發(fā)基本成型,功能基本完備,研發(fā)者能提供可測試版本。

            測試的相關(guān)協(xié)作:

            發(fā)送測試文檔給開發(fā)者,同時抄送給項目經(jīng)理、產(chǎn)品經(jīng)理、測試等等相關(guān)人員;遇到爭議主動找項目經(jīng)理、產(chǎn)品經(jīng)理等相關(guān)領(lǐng)導(dǎo)協(xié)商。

            測試依據(jù)的文檔:

            做過測試工程師的應(yīng)該都知道,測試工程師是根據(jù)自己編寫的測試用例(精簡測試用例、詳細測試用例)來測試,一般情況下,設(shè)計師根據(jù)精簡測試用例文檔來測試就好了,設(shè)計師只是要依據(jù)某個使用過程來試用并發(fā)現(xiàn)問題。當(dāng)然如果設(shè)計師愿意寫幾個主要使用場景,然后根據(jù)自己的使用場景來測試更好,不過要注意自己的使用場景和設(shè)計文檔保持一致。

            讓產(chǎn)品設(shè)計師跟蹤測試的好處:

            1、設(shè)計師比測試工程師更多關(guān)注可用性,可以保證產(chǎn)品的高質(zhì)量。畢竟設(shè)計師對評判產(chǎn)品好壞較強的審美能力。

            2、遇到問題可以直接給出解決方案,效率高。

            3、可以看到更多的設(shè)計問題,便于及時補充和修正設(shè)計文檔。設(shè)計師可以鍛煉細節(jié)關(guān)注能力,積累更多經(jīng)驗。(本條的收獲很大呀!)

            4、設(shè)計師可以很好的參與到開發(fā)中去。

            分享一下自己做跟蹤測試的經(jīng)驗和教訓(xùn)

            1、設(shè)計師在跟蹤測試之前應(yīng)做足的工作:確保設(shè)計文檔寫的更詳細和易讀,確保無主要邏輯缺失。最好做出原型并依據(jù)原型多體驗幾遍,或者邀請其他設(shè)計師一起來體驗,爭取在開發(fā)前發(fā)現(xiàn)更多的問題,確保文檔質(zhì)量。否則,一旦開發(fā)出現(xiàn)問題或者開發(fā)進度延遲,會把全部責(zé)任推到產(chǎn)品設(shè)計師身上。開發(fā)者會說:“文檔沒寫”或“文檔沒寫明白,看不懂。”遇到這樣的情況,設(shè)計師百口難辨,設(shè)計師的確是有責(zé)任的(雖然不是全部)。

            2、搞好關(guān)系,不要直接指責(zé)開發(fā)人員或開發(fā)中的問題。理智的做法應(yīng)該是:”客觀的表述操作,客觀的提出正確的方案。”描述問題時不要有任何情緒,或者可能讓合作者產(chǎn)生“逆反心理”的語氣。比如:“竟然”“居然”“錯誤”等,當(dāng)然適當(dāng)?shù)目洫勔幌乱彩强梢缘摹?/p>

            3、在遇到爭議時,通過正確的渠道解決問題,主動通過雙方主管協(xié)商解決,開發(fā)人員不會聽你的,不要試圖說服他們,和他們爭論的結(jié)果只會讓他們記恨你,還有肯能找機會給你穿小鞋,設(shè)計師爭取避免這個問題。遇到問題要先學(xué)會傾聽,然后才有可能正確處理問題。否則容易產(chǎn)生誤解,讓別人誤以為你不好合作或不好溝通(但實際上你是為產(chǎn)品質(zhì)量而掙)。

            4、和開發(fā)人員、測試人員保持緊密的溝通,提高解決問題的速度;有需求變動或文檔改動要迅速反應(yīng),并及時通知大家。否則,如果研發(fā)沒有按照變動來修改,會怪罪你沒有及時通知。

            ……,更多感受還需到工作中去體會。

            小結(jié):說了那么多,這樣做還得公司高層大力支持并推行為前提;設(shè)計師要真正處理好各種關(guān)系還得自己實際去體會,畢竟每個公司的情況都不盡相同;設(shè)計師可以獲得很多,更清楚要向開發(fā)者“表達什么?如果表達?”。

          posted @ 2012-05-29 10:06 順其自然EVO 閱讀(153) | 評論 (0)編輯 收藏

          性能測試新手誤區(qū)(三):用戶數(shù)與壓力

           性能測試新手誤區(qū)(一):找不到測試點,不知為何而測

            性能測試新手誤區(qū)(二):為什么我模擬的百萬測試數(shù)據(jù)是無效的?

            同樣的項目、同樣的性能需求,讓不同的測試人員來測,會是相同的結(jié)果么?

            假設(shè)有這樣一個小論壇,性能測試人員得到的需求是“支持并發(fā)50人,響應(yīng)時間要在3秒以內(nèi)”,性能測試人員A和B同時開始進行性能測試(各做各的)。

            只考慮發(fā)帖這個操作,A設(shè)計的測試場景是50人并發(fā)發(fā)帖,得到的測試結(jié)果是平均完成時間是5秒。于是他提出了這個問題,認為系統(tǒng)沒有達到性能期望,需要開發(fā)人員進行優(yōu)化。

            B設(shè)計的測試場景是,50個人在線,并且在5分鐘內(nèi)每人發(fā)一個帖子,也就是1分鐘內(nèi)有10個人發(fā)帖子,最后得到的測試結(jié)果是平均完成時間2秒。于是他的結(jié)論是系統(tǒng)通過性能測試,可以滿足上線的壓力。

            兩個人得到了不同的測試結(jié)果,完全相反的測試結(jié)論,誰做錯了?

            或許這個例子太極端,絕對并發(fā)和平均分布的訪問壓力當(dāng)然是截然不同的,那我們再來看個更真實的例子。

            還是一個小論壇,需求是“100人在線時,頁面響應(yīng)時間要小于3秒”。A和B又同時開工了,這時他們都成長了,經(jīng)驗更加豐富了,也知道了要設(shè)計出更符合實際的測試場景。假設(shè)他們都確認了用戶的操作流程為“登錄-進入子論壇-(瀏覽列表-瀏覽帖子)×10-發(fā)帖”,即每個用戶看10個帖子、發(fā)一個帖子。于是他們都錄制出了同樣的測試腳本。

            A認為,每個用戶的操作,一般間隔30s比較合適,于是他在腳本中的每兩個事務(wù)之間加上了30秒的等待(思考時間)。

            B想了想自己看論壇時的情景,好像平均每次鼠標(biāo)點擊要間隔1分鐘,于是他在腳本中的每兩個事務(wù)之間加上了1分鐘的等待。

            他們都認為自己的測試場景比較接近實際情況,可惜測試結(jié)果又是不同的,很顯然A場景的壓力是B的兩倍。那誰錯了呢?或者有人說是需求不明確導(dǎo)致的,那么你需要什么樣的需求呢?

            看看我隨手在網(wǎng)上(51testing)找的提問吧,和上面的內(nèi)容如出一轍。一定有很多的性能測試人員每天接到的就是這種需求,又這樣就開展了測試,結(jié)果可想而知。

            這里我想問幾個問題,希望各位看完了上面的小例子后想一想:

            如果有另一個人和你測同樣的系統(tǒng),你們的測試結(jié)果會一致么?

            如果不一致,那么誰是正確的?

            如何證明測試結(jié)果是有效的?

            如果你有了一些疑惑,對之前的測試結(jié)果少了一些自信,那么請繼續(xù)。

            服務(wù)器視角 vs. 用戶視角

            性能測試中非常重要的一塊內(nèi)容就是模擬預(yù)期的壓力,測試系統(tǒng)運行在此壓力下,用戶的體驗是什么樣的。

            那么壓力是什么?壓力是服務(wù)器在不斷的處理事情、甚至是同時處理很多事情。壓力是服務(wù)器直接處理的“事情”,而不是遠在網(wǎng)絡(luò)另一端的用戶。

          下圖中,每一個顏色的線段代表一種操作。在任意一個時刻,服務(wù)器都知道它有10個事務(wù)需要處理,這10個事務(wù)也是有10個用戶產(chǎn)生的。但它不知道的是,整個時間段內(nèi)的所有事務(wù),是由多少個用戶與系統(tǒng)交互而產(chǎn)生的。

            這句話好像有點繞,我再試著更形象的解釋一下。時刻1,10個當(dāng)前事務(wù)是由10個用戶發(fā)起的。時刻2,依然是10個正在進行的事務(wù),但可能是完全不同的10個人發(fā)起的。在這段時間內(nèi),服務(wù)器每一個時刻都在處理10個事務(wù),但是參與了這個交互過程(對服務(wù)器產(chǎn)生壓力)的人可能會達到上百個,也可能只有最開始的10個。

            那么,對于服務(wù)器來說,壓力是什么呢?顯然只是每時刻這10個同時處理的事務(wù),而到底是有10個人還是1000個人,區(qū)別不大(暫不考慮session等問題)。

            下面再從用戶的視角來看看。實際的情況中,不可能出現(xiàn)很多用戶同一時刻開始進行操作的場景,而是有一定的時間順序的。正如下圖所示,在這個時間段內(nèi),一共有23個用戶進行了操作。

            但是服務(wù)器能看到這些用戶么?它知道的只是某一個時間點上,有多少個正在執(zhí)行的事務(wù)。大家可以數(shù)一下,此圖中任意時刻的并發(fā)事務(wù)依然是10個。

            其實這兩個圖描述的本來就是同一個場景,只不過觀察者的視角不同罷了。

            那么大家想想,在性能需求中最常見到的“并發(fā)用戶”到底是指的什么呢? 并發(fā)用戶

            很多使用“并發(fā)用戶”這個詞的人,并沒有從服務(wù)器視角進行考慮。他們想的是坐在電腦前使用這個系統(tǒng)、對系統(tǒng)產(chǎn)生壓力的人的個數(shù)?;谶@個原因,我很少使用這個容易讓人誤解的詞匯,而是進行了更細的劃分。主要有這么幾個:系統(tǒng)用戶數(shù)(注冊用戶數(shù))、在線用戶數(shù)(相對并發(fā)用戶數(shù))、絕對并發(fā)用戶數(shù)。

            上面幾個例子中所說的“并發(fā)用戶”,實際就是在線用戶數(shù)。其實我更喜歡叫做相對并發(fā)用戶數(shù),因為這個詞更容易讓人感受到“壓力”。相對并發(fā)用戶數(shù)指的是,在一個時間段內(nèi),與服務(wù)器進行了交互、對服務(wù)器產(chǎn)生了壓力的用戶的數(shù)量。這個時間段,可以是一天,也可以是一個小時。而需求人員必須要描述的,也正是這個內(nèi)容。

            而絕對并發(fā)用戶,主要是針對某一個操作進行測試,即多個用戶同一時刻發(fā)起相同請求。可以用來驗證是否存在并發(fā)邏輯上的處理問題,如線程不安全、死鎖等問題;也可提供一些性能上的參考信息,比如1個用戶需要1秒,而10個用戶并發(fā)卻需要30秒,那很可能就會有問題,需要進行關(guān)注,因為10個用戶請求排隊處理也應(yīng)該只需要10秒啊。但這種絕對并發(fā)的測試,同實際壓力下的用戶體驗關(guān)系不大。

            再回到相對并發(fā)這個概念上來,它與服務(wù)器的壓力到底是什么關(guān)系呢?如果你理解了前面的所有內(nèi)容,那么就會知道這兩者其實沒有直接聯(lián)系(當(dāng)然了,同一個測試用例中,肯定是用戶數(shù)越多壓力越大)。也就是說,你得到的這種性能需求,是無法知道服務(wù)器到底要承受多大壓力的。

            那么如何開展性能測試?

            如何模擬壓力

            既然我們知道了所謂的壓力其實是從服務(wù)器視角來講的,服務(wù)器要處理的事務(wù)才是壓力,那么我們就從這出發(fā),來探尋一下性能測試需要的信息。依然用之前的小論壇為例,我們需要測試活躍用戶為500人時,系統(tǒng)的性能是否能還能提供良好的用戶感受。

            假設(shè)現(xiàn)在的活躍用戶有50個人(或者通過另一個類似的系統(tǒng)來推算也行),平均每天總的發(fā)帖量是50條、瀏覽帖子500次,也就是每人每天發(fā)一個帖子、瀏覽十個帖子(為了方便講解,假設(shè)論壇只有這兩個基本功能)。那么我們就可以推算,活躍用戶達到500時,每天的業(yè)務(wù)量也會成比例的增長,也就是平均每天會產(chǎn)生500個新帖子、瀏覽帖子5000次。

            進一步分析數(shù)據(jù),又發(fā)現(xiàn)。用戶使用論壇的時間段非常集中,基本集中在中午11點到1點和晚上18點到20點。也就是說每天的這些業(yè)務(wù),實際是分布在4個小時中完成的。

            那我們的測試場景,就是要用500個用戶在4小時內(nèi)完成“每人發(fā)一個帖子、瀏覽十個帖子”的工作量。

            注意上面的兩處,“平均每天……”、“分布在4個小時……”。敏感的測試人員應(yīng)該能發(fā)現(xiàn),這個場景測的是平均壓力,也就是一個系統(tǒng)最平常一天的使用壓力,我喜歡稱之為日常壓力。

            顯然,除了日常壓力,系統(tǒng)還會有壓力更大的使用場景,比如某天發(fā)生了一件重要的事情,那么用戶就會更加熱烈的進行討論。這個壓力,我習(xí)慣叫做高峰期壓力,需要專門設(shè)計一個測試場景。

            這個場景,需要哪些數(shù)據(jù)呢,我們依然可以從現(xiàn)有的數(shù)據(jù)進行分析。比如上面提到的是“平均每天總的發(fā)帖量……”,那么這次我們就要查到過去最高一日的業(yè)務(wù)量。“分布在4個小時”也需要進行相應(yīng)的修改,比如查查歷史分布圖是否有更為集中的分布,或者用更簡單通用的80-20原則,80%的工作在20%的時間內(nèi)完成。根據(jù)這些數(shù)據(jù)可以再做適當(dāng)?shù)恼{(diào)整,設(shè)計出高峰期的測試場景。

            實際工作中可能還需要更多的測試場景,比如峰值壓力場景。什么是峰值壓力呢,比如一個銀行網(wǎng)站,可能會由于發(fā)布一條重磅消息使訪問量驟增,這個突發(fā)的壓力也是性能測試人員需要考慮的。

            需要注意高峰期壓力和峰值壓力的區(qū)別,高峰期壓力是指系統(tǒng)正常的、預(yù)期內(nèi)壓力的一個高峰。而峰值壓力是指那些不在正常預(yù)期內(nèi)的壓力,可能幾年才出現(xiàn)一次。

            這里只是舉了個最簡單的例子,實際工作遠比這復(fù)雜的多。需要哪些數(shù)據(jù)、如何獲取,很可能要取得這些數(shù)據(jù)就要花費很大的功夫。這其實就涉及到了一個很重要的內(nèi)容,用戶模型和壓力模型的建立,以后會有專門的文章進行講述。

            為什么要花這么大的精力來收集這些信息呢?是因為只有通過這些有效的數(shù)據(jù),才能準(zhǔn)確的去模擬用戶場景,準(zhǔn)確的模擬壓力,獲取到更加真實的用戶體驗。只有這樣,“不同的測試人員,測出相同的結(jié)果”才會有可能實現(xiàn),而且結(jié)果都是準(zhǔn)確有效的。

            要點回顧

            ● 最后通過幾個小問題來總結(jié)回顧一下:

            ● 你真的理解“并發(fā)用戶”的意義么?

            ● 什么是用戶視角和服務(wù)器視角?

            ● 什么是壓力?

            ● 如何模擬預(yù)期壓力?

          相關(guān)鏈接:

          性能測試新手誤區(qū)(一):找不到測試點,不知為何而測

          性能測試新手誤區(qū)(二):為什么我模擬的百萬測試數(shù)據(jù)是無效的?

          posted @ 2012-05-29 10:01 順其自然EVO 閱讀(211) | 評論 (0)編輯 收藏

          如何書寫優(yōu)雅、漂亮的SQL腳本?

               摘要: 本篇來聊聊如何書寫漂亮、整潔、優(yōu)雅的SQL腳本,下面這些是我個人總結(jié)、整理出來的。姑且做個拋磚引玉吧,呵呵,歡迎大家一起來討論?! ∥覀兪紫葋砜纯匆欢蝿?chuàng)建數(shù)據(jù)表的腳本(如下所示),你是否覺得有什么不妥或是不足呢?如果是你,你怎樣書寫呢?Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHig...  閱讀全文

          posted @ 2012-05-29 09:53 順其自然EVO 閱讀(283) | 評論 (0)編輯 收藏

          Java程序員集合框架面試題

           Java集合框架是最常被問到的Java面試問題,要理解Java技術(shù)強大特性就有必要掌握集合框架。這里有一些實用問題,常在核心Java面試中問到。

            1、什么是Java集合API

            Java集合框架API是用來表示和操作集合的統(tǒng)一框架,它包含接口、實現(xiàn)類、以及幫助程序員完成一些編程的算法。簡言之,API在上層完成以下幾件事:

            ● 編程更加省力,提高城程序速度和代碼質(zhì)量

            ● 非關(guān)聯(lián)的API提高互操作性

            ● 節(jié)省學(xué)習(xí)使用新API成本

            ● 節(jié)省設(shè)計新API的時間

            ● 鼓勵、促進軟件重用

            具體來說,有6個集合接口,最基本的是Collection接口,由三個接口Set、List、SortedSet繼承,另外兩個接口是Map、SortedMap,這兩個接口不繼承Collection,表示映射而不是真正的集合。

            2、什么是Iterator

            一些集合類提供了內(nèi)容遍歷的功能,通過java.util.Iterator接口。這些接口允許遍歷對象的集合。依次操作每個元素對象。當(dāng)使用 Iterators時,在獲得Iterator的時候包含一個集合快照。通常在遍歷一個Iterator的時候不建議修改集合本省。

            3、Iterator與ListIterator有什么區(qū)別?

            Iterator:只能正向遍歷集合,適用于獲取移除元素。ListIerator:繼承Iterator,可以雙向列表的遍歷,同樣支持元素的修改。

            4、什么是HaspMap和Map?

            Map是接口,Java 集合框架中一部分,用于存儲鍵值對,HashMap是用哈希算法實現(xiàn)Map的類。

            5、HashMap與HashTable有什么區(qū)別?對比Hashtable VS HashMap

            兩者都是用key-value方式獲取數(shù)據(jù)。Hashtable是原始集合類之一(也稱作遺留類)。HashMap作為新集合框架的一部分在Java2的1.2版本中加入。它們之間有一下區(qū)別:

            ● HashMap和Hashtable大致是等同的,除了非同步和空值(HashMap允許null值作為key和value,而Hashtable不可以)。

            ● HashMap沒法保證映射的順序一直不變,但是作為HashMap的子類LinkedHashMap,如果想要預(yù)知的順序迭代(默認按照插入順序),你可以很輕易的置換為HashMap,如果使用Hashtable就沒那么容易了。

            ● HashMap不是同步的,而Hashtable是同步的。

            ● 迭代HashMap采用快速失敗機制,而Hashtable不是,所以這是設(shè)計的考慮點。

            6、在Hashtable上下文中同步是什么意思?

            同步意味著在一個時間點只能有一個線程可以修改哈希表,任何線程在執(zhí)行hashtable的更新操作前需要獲取對象鎖,其他線程等待鎖的釋放。

            7、什么叫做快速失敗特性

            從高級別層次來說快速失敗是一個系統(tǒng)或軟件對于其故障做出的響應(yīng)。一個快速失敗系統(tǒng)設(shè)計用來即時報告可能會導(dǎo)致失敗的任何故障情況,它通常用來停止正常的操作而不是嘗試?yán)^續(xù)做可能有缺陷的工作。當(dāng)有問題發(fā)生時,快速失敗系統(tǒng)即時可見地發(fā)錯錯誤告警。在Java中,快速失敗與iterators有關(guān)。如果一個iterator在集合對象上創(chuàng)建了,其它線程欲“結(jié)構(gòu)化”的修改該集合對象,并發(fā)修改異常 (ConcurrentModificationException) 拋出。

            8、怎樣使Hashmap同步?

            HashMap可以通過Map m = Collections.synchronizedMap(hashMap)來達到同步的效果。

            9、什么時候使用Hashtable,什么時候使用HashMap

            基本的不同點是Hashtable同步HashMap不是的,所以無論什么時候有多個線程訪問相同實例的可能時,就應(yīng)該使用Hashtable,反之使用HashMap。非線程安全的數(shù)據(jù)結(jié)構(gòu)能帶來更好的性能。

            如果在將來有一種可能—你需要按順序獲得鍵值對的方案時,HashMap是一個很好的選擇,因為有HashMap的一個子類 LinkedHashMap。所以如果你想可預(yù)測的按順序迭代(默認按插入的順序),你可以很方便用LinkedHashMap替換HashMap。反觀要是使用的Hashtable就沒那么簡單了。同時如果有多個線程訪問HashMap,Collections.synchronizedMap()可以代替,總的來說HashMap更靈活。

            10、為什么Vector類認為是廢棄的或者是非官方地不推薦使用?或者說為什么我們應(yīng)該一直使用ArrayList而不是Vector

            你應(yīng)該使用ArrayList而不是Vector是因為默認情況下你是非同步訪問的,Vector同步了每個方法,你幾乎從不要那樣做,通常有想要同步的是整個操作序列。同步單個的操作也不安全(如果你迭代一個Vector,你還是要加鎖,以避免其它線程在同一時刻改變集合).而且效率更慢。當(dāng)然同樣有鎖的開銷即使你不需要,這是個很糟糕的方法在默認情況下同步訪問。你可以一直使用Collections.sychronizedList來裝飾一個集合。

            事實上Vector結(jié)合了“可變數(shù)組”的集合和同步每個操作的實現(xiàn)。這是另外一個設(shè)計上的缺陷。Vector還有些遺留的方法在枚舉和元素獲取的方法,這些方法不同于List接口,如果這些方法在代碼中程序員更趨向于想用它。盡管枚舉速度更快,但是他們不能檢查如果集合在迭代的時候修改了,這樣將導(dǎo)致問題。盡管以上諸多原因,oracle也從沒宣稱過要廢棄Vector。

          posted @ 2012-05-29 09:44 順其自然EVO 閱讀(211) | 評論 (0)編輯 收藏

          關(guān)于Java 23種設(shè)計模式的有趣見解

            在網(wǎng)絡(luò)上流暢很廣的一篇舊文,暫時沒找到原作者,作者用輕松的語言,形象解釋了 23 種模式,有很好的啟發(fā)作用。

            創(chuàng)建型模式

            1、FACTORY—追MM少不了請吃飯了,麥當(dāng)勞的雞翅和肯德基的雞翅都是MM愛吃的東西,雖然口味有所不同,但不管你帶MM去麥當(dāng)勞或肯德基,只管向服務(wù)員說“來四個雞翅”就行了。麥當(dāng)勞和肯德基就是生產(chǎn)雞翅的Factory

            工廠模式:客戶類和工廠類分開。消費者任何時候需要某種產(chǎn)品,只需向工廠請求即可。消費者無須修改就可以接納新產(chǎn)品。缺點是當(dāng)產(chǎn)品修改時,工廠類也要做相應(yīng)的修改。如:如何創(chuàng)建及如何向客戶端提供。

            2、BUILDER—MM最愛聽的就是“我愛你”這句話了,見到不同地方的MM,要能夠用她們的方言跟她說這句話哦,我有一個多種語言翻譯機,上面每種語言都有一個按鍵,見到MM我只要按對應(yīng)的鍵,它就能夠用相應(yīng)的語言說出“我愛你”這句話了,國外的MM也可以輕松搞掂,這就是我的“我愛你”builder。(這一定比美軍在伊拉克用的翻譯機好賣)

            建造模式:將產(chǎn)品的內(nèi)部表象和產(chǎn)品的生成過程分割開來,從而使一個建造過程生成具有不同的內(nèi)部表象的產(chǎn)品對象。建造模式使得產(chǎn)品內(nèi)部表象可以獨立的變化,客戶不必知道產(chǎn)品內(nèi)部組成的細節(jié)。建造模式可以強制實行一種分步驟進行的建造過程。

            3、FACTORY METHOD—請MM去麥當(dāng)勞吃漢堡,不同的MM有不同的口味,要每個都記住是一件煩人的事情,我一般采用Factory Method模式,帶著MM到服務(wù)員那兒,說“要一個漢堡”,具體要什么樣的漢堡呢,讓MM直接跟服務(wù)員說就行了。

            工廠方法模式:核心工廠類不再負責(zé)所有產(chǎn)品的創(chuàng)建,而是將具體創(chuàng)建的工作交給子類去做,成為一個抽象工廠角色,僅負責(zé)給出具體工廠類必須實現(xiàn)的接口,而不接觸哪一個產(chǎn)品類應(yīng)當(dāng)被實例化這種細節(jié)。

            4、PROTOTYPE—跟MM用QQ聊天,一定要說些深情的話語了,我搜集了好多肉麻的情話,需要時只要copy出來放到QQ里面就行了,這就是我的情話prototype了。(100塊錢一份,你要不要)

            原始模型模式:通過給出一個原型對象來指明所要創(chuàng)建的對象的類型,然后用復(fù)制這個原型對象的方法創(chuàng)建出更多同類型的對象。原始模型模式允許動態(tài)的增加或減少產(chǎn)品類,產(chǎn)品類不需要非得有任何事先確定的等級結(jié)構(gòu),原始模型模式適用于任何的等級結(jié)構(gòu)。缺點是每一個類都必須配備一個克隆方法。

            5、SINGLETON—俺有6個漂亮的老婆,她們的老公都是我,我就是我們家里的老公Sigleton,她們只要說道“老公”,都是指的同一個人,那就是我(剛才做了個夢啦,哪有這么好的事)

            單例模式:單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例單例模式。單例模式只應(yīng)在有真正的“單一實例”的需求時才可使用。

            結(jié)構(gòu)型模式

            6、ADAPTER—在朋友聚會上碰到了一個美女Sarah,從香港來的,可我不會說粵語,她不會說普通話,只好求助于我的朋友kent了,他作為我和Sarah之間的Adapter,讓我和Sarah可以相互交談了(也不知道他會不會耍我)

            適配器(變壓器)模式:把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口原因不匹配而無法一起工作的兩個類能夠一起工作。適配類可以根據(jù)參數(shù)返還一個合適的實例給客戶端。

            7、BRIDGE—早上碰到MM,要說早上好,晚上碰到MM,要說晚上好;碰到MM穿了件新衣服,要說你的衣服好漂亮哦,碰到MM新做的發(fā)型,要說你的頭發(fā)好漂亮哦。不要問我“早上碰到MM新做了個發(fā)型怎么說”這種問題,自己用BRIDGE組合一下不就行了

            橋梁模式:將抽象化與實現(xiàn)化脫耦,使得二者可以獨立的變化,也就是說將他們之間的強關(guān)聯(lián)變成弱關(guān)聯(lián),也就是指在一個軟件系統(tǒng)的抽象化和實現(xiàn)化之間使用組合/聚合關(guān)系而不是繼承關(guān)系,從而使兩者可以獨立的變化。

            8、COMPOSITE—Mary今天過生日。“我過生日,你要送我一件禮物。”“嗯,好吧,去商店,你自己挑。”“這件T恤挺漂亮,買,這條裙子好看,買,這個包也不錯,買。”“喂,買了三件了呀,我只答應(yīng)送一件禮物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻煩你包起來。”“……”,MM都會用Composite模式了,你會了沒有?

            合成模式:合成模式將對象組織到樹結(jié)構(gòu)中,可以用來描述整體與部分的關(guān)系。合成模式就是一個處理對象的樹結(jié)構(gòu)的模式。合成模式把部分與整體的關(guān)系用樹結(jié)構(gòu)表示出來。合成模式使得客戶端把一個個單獨的成分對象和由他們復(fù)合而成的合成對象同等看待。

            9、DECORATOR—Mary過完輪到Sarly過生日,還是不要叫她自己挑了,不然這個月伙食費肯定玩完,拿出我去年在華山頂上照的照片,在背面寫上“最好的的禮物,就是愛你的Fita”,再到街上禮品店買了個像框(賣禮品的MM也很漂亮哦),再找隔壁搞美術(shù)設(shè)計的Mike設(shè)計了一個漂亮的盒子裝起來……,我們都是Decorator,最終都在修飾我這個人呀,怎么樣,看懂了嗎?

          裝飾模式:裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關(guān)系的一個替代方案,提供比繼承更多的靈活性。動態(tài)給一個對象增加功能,這些功能可以再動態(tài)的撤消。增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能。

            10、FACADE—我有一個專業(yè)的Nikon相機,我就喜歡自己手動調(diào)光圈、快門,這樣照出來的照片才專業(yè),但MM可不懂這些,教了半天也不會。幸好相機有Facade設(shè)計模式,把相機調(diào)整到自動檔,只要對準(zhǔn)目標(biāo)按快門就行了,一切由相機自動調(diào)整,這樣MM也可以用這個相機給我拍張照片了。

            門面模式:外部與一個子系統(tǒng)的通信必須通過一個統(tǒng)一的門面對象進行。門面模式提供一個高層次的接口,使得子系統(tǒng)更易于使用。每一個子系統(tǒng)只有一個門面類,而且此門面類只有一個實例,也就是說它是一個單例模式。但整個系統(tǒng)可以有多個門面類。

            11、FLYWEIGHT—每天跟MM發(fā)短信,手指都累死了,最近買了個新手機,可以把一些常用的句子存在手機里,要用的時候,直接拿出來,在前面加上MM的名字就可以發(fā)送了,再不用一個字一個字敲了。共享的句子就是Flyweight,MM的名字就是提取出來的外部特征,根據(jù)上下文情況使用。

            享元模式:FLYWEIGHT在拳擊比賽中指最輕量級。享元模式以共享的方式高效的支持大量的細粒度對象。享元模式能做到共享的關(guān)鍵是區(qū)分內(nèi)蘊狀態(tài)和外蘊狀態(tài)。內(nèi)蘊狀態(tài)存儲在享元內(nèi)部,不會隨環(huán)境的改變而有所不同。外蘊狀態(tài)是隨環(huán)境的改變而改變的。外蘊狀態(tài)不能影響內(nèi)蘊狀態(tài),它們是相互獨立的。將可以共享的狀態(tài)和不可以共享的狀態(tài)從常規(guī)類中區(qū)分開來,將不可以共享的狀態(tài)從類里剔除出去。客戶端不可以直接創(chuàng)建被共享的對象,而應(yīng)當(dāng)使用一個工廠對象負責(zé)創(chuàng)建被共享的對象。享元模式大幅度的降低內(nèi)存中對象的數(shù)量。

            12、PROXY—跟MM在網(wǎng)上聊天,一開頭總是“hi,你好”,“你從哪兒來呀?”“你多大了?”“身高多少呀?”這些話,真煩人,寫個程序做為我的Proxy吧,凡是接收到這些話都設(shè)置好了自動的回答,接收到其他的話時再通知我回答,怎么樣,酷吧。

            代理模式:代理模式給某一個對象提供一個代理對象,并由代理對象控制對源對象的引用。代理就是一個人或一個機構(gòu)代表另一個人或者一個機構(gòu)采取行動。某些情況下,客戶不想或者不能夠直接引用一個對象,代理對象可以在客戶和目標(biāo)對象直接起到中介的作用。客戶端分辨不出代理主題對象與真實主題對象。代理模式可以并不知道真正的被代理對象,而僅僅持有一個被代理對象的接口,這時候代理對象不能夠創(chuàng)建被代理對象,被代理對象必須有系統(tǒng)的其他角色代為創(chuàng)建并傳入。

            行為模式

            13、CHAIN OF RESPONSIBLEITY—晚上去上英語課,為了好開溜坐到了最后一排,哇,前面坐了好幾個漂亮的MM哎,找張紙條,寫上“Hi,可以做我的女朋友嗎?如果不愿意請向前傳”,紙條就一個接一個的傳上去了,糟糕,傳到第一排的MM把紙條傳給老師了,聽說是個老處女呀,快跑!

            責(zé)任鏈模式:在責(zé)任鏈模式中,很多對象由每一個對象對其下家的引用而接

            起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。客戶并不知道鏈上的哪一個對象最終處理這個請求,系統(tǒng)可以在不影響客戶端的情況下動態(tài)的重新組織鏈和分配責(zé)任。處理者有兩個選擇:承擔(dān)責(zé)任或者把責(zé)任推給下家。一個請求可以最終不被任何接收端對象所接受。

            14、COMMAND—俺有一個MM家里管得特別嚴(yán),沒法見面,只好借助于她弟弟在我們倆之間傳送信息,她對我有什么指示,就寫一張紙條讓她弟弟帶給我。這不,她弟弟又傳送過來一個COMMAND,為了感謝他,我請他吃了碗雜醬面,哪知道他說:“我同時給我姐姐三個男朋友送COMMAND,就數(shù)你最小氣,才請我吃面。”,:-(

            命令模式:命令模式把一個請求或者操作封裝到一個對象中。命令模式把發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開,委派給不同的對象。命令模式允許請求的一方和發(fā)送的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎么被接收,以及操作是否執(zhí)行,何時被執(zhí)行以及是怎么被執(zhí)行的。系統(tǒng)支持命令的撤消。

            15、INTERPRETER—俺有一個《泡MM真經(jīng)》,上面有各種泡MM的攻略,比如說去吃西餐的步驟、去看電影的方法等等,跟MM約會時,只要做一個Interpreter,照著上面的腳本執(zhí)行就可以了。

            解釋器模式:給定一個語言后,解釋器模式可以定義出其文法的一種表示,并同時提供一個解釋器??蛻舳丝梢允褂眠@個解釋器來解釋這個語言中的句子。解釋器模式將描述怎樣在有了一個簡單的文法后,使用模式設(shè)計解釋這些語句。在解釋器模式里面提到的語言是指任何解釋器對象能夠解釋的任何組合。在解釋器模式中需要定義一個代表文法的命令類的等級結(jié)構(gòu),也就是一系列的組合規(guī)則。每一個命令對象都有一個解釋方法,代表對命令對象的解釋。命令對象的等級結(jié)構(gòu)中的對象的任何排列組合都是一個語言。

            16、ITERATOR—我愛上了Mary,不顧一切的向她求婚。

            Mary:“想要我跟你結(jié)婚,得答應(yīng)我的條件”

            我:“什么條件我都答應(yīng),你說吧”

            Mary:“我看上了那個一克拉的鉆石”

            我:“我買,我買,還有嗎?”

            Mary:“我看上了湖邊的那棟別墅”

            我:“我買,我買,還有嗎?”

            Mary:“你的小弟弟必須要有50cm長”

            我腦袋嗡的一聲,坐在椅子上,一咬牙:“我剪,我剪,還有嗎?”

           迭代子模式:迭代子模式可以順序訪問一個聚集中的元素而不必暴露聚集的內(nèi)部表象。多個對象聚在一起形成的總體稱之為聚集,聚集對象是能夠包容一組對象的容器對象。迭代子模式將迭代邏輯封裝到一個獨立的子對象中,從而與聚集本身隔開。迭代子模式簡化了聚集的界面。每一個聚集對象都可以有一個或一個以上的迭代子對象,每一個迭代子的迭代狀態(tài)可以是彼此獨立的。迭代算法可以獨立于聚集角色變化。

            17、MEDIATOR—四個MM打麻將,相互之間誰應(yīng)該給誰多少錢算不清楚了,幸虧當(dāng)時我在旁邊,按照各自的籌碼數(shù)算錢,賺了錢的從我這里拿,賠了錢的也付給我,一切就OK啦,俺得到了四個MM的電話。

            調(diào)停者模式:調(diào)停者模式包裝了一系列對象相互作用的方式,使得這些對象不必相互明顯作用。從而使他們可以松散偶合。當(dāng)某些對象之間的作用發(fā)生改變時,不會立即影響其他的一些對象之間的作用。保證這些作用可以彼此獨立的變化。調(diào)停者模式將多對多的相互作用轉(zhuǎn)化為一對多的相互作用。調(diào)停者模式將對象的行為和協(xié)作抽象化,把對象在小尺度的行為上與其他對象的相互作用分開處理。

            18、MEMENTO—同時跟幾個MM聊天時,一定要記清楚剛才跟MM說了些什么話,不然MM發(fā)現(xiàn)了會不高興的哦,幸虧我有個備忘錄,剛才與哪個MM說了什么話我都拷貝一份放到備忘錄里面保存,這樣可以隨時察看以前的記錄啦。

            備忘錄模式:備忘錄對象是一個用來存儲另外一個對象內(nèi)部狀態(tài)的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一個對象的狀態(tài)捉住,并外部化,存儲起來,從而可以在將來合適的時候把這個對象還原到存儲起來的狀態(tài)。

            19、OBSERVER—想知道咱們公司最新MM情報嗎?加入公司的MM情報郵件組就行了,tom負責(zé)搜集情報,他發(fā)現(xiàn)的新情報不用一個一個通知我們,直接發(fā)布給郵件組,我們作為訂閱者(觀察者)就可以及時收到情報啦

            觀察者模式:觀察者模式定義了一種一隊多的依賴關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象。這個主題對象在狀態(tài)上發(fā)生變化時,會通知所有觀察者對象,使他們能夠自動更新自己。

            20、STATE—跟MM交往時,一定要注意她的狀態(tài)哦,在不同的狀態(tài)時她的行為會有不同,比如你約她今天晚上去看電影,對你沒興趣的MM就會說“有事情啦”,對你不討厭但還沒喜歡上的MM就會說“好啊,不過可以帶上我同事么?”,已經(jīng)喜歡上你的MM就會說“幾點鐘?看完電影再去泡吧怎么樣?”,當(dāng)然你看電影過程中表現(xiàn)良好的話,也可以把MM的狀態(tài)從不討厭不喜歡變成喜歡哦。

            狀態(tài)模式:狀態(tài)模式允許一個對象在其內(nèi)部狀態(tài)改變的時候改變行為。這個對象看上去象是改變了它的類一樣。狀態(tài)模式把所研究的對象的行為包裝在不同的狀態(tài)對象里,每一個狀態(tài)對象都屬于一個抽象狀態(tài)類的一個子類。狀態(tài)模式的意圖是讓一個對象在其內(nèi)部狀態(tài)改變的時候,其行為也隨之改變。狀態(tài)模式需要對每一個系統(tǒng)可能取得的狀態(tài)創(chuàng)立一個狀態(tài)類的子類。當(dāng)系統(tǒng)的狀態(tài)變化時,系統(tǒng)便改變所選的子類。

            21、STRATEGY—跟不同類型的MM約會,要用不同的策略,有的請電影比較好,有的則去吃小吃效果不錯,有的去海邊浪漫最合適,單目的都是為了得到MM的芳心,我的追MM錦囊中有好多Strategy哦。

            策略模式:策略模式針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發(fā)生變化。策略模式把行為和環(huán)境分開。環(huán)境類負責(zé)維持和查詢行為類,各種算法在具體的策略類中提供。由于算法和環(huán)境獨立開來,算法的增減,修改都不會影響到環(huán)境和客戶端。

            22、TEMPLATE METHOD——看過《如何說服女生上床》這部經(jīng)典文章嗎?女生從認識到上床的不變的步驟分為巧遇、打破僵局、展開追求、接吻、前戲、動手、愛撫、進去八大步驟(Template method),但每個步驟針對不同的情況,都有不一樣的做法,這就要看你隨機應(yīng)變啦(具體實現(xiàn));

            模板方法模式:模板方法模式準(zhǔn)備一個抽象類,將部分邏輯以具體方法以及具體構(gòu)造子的形式實現(xiàn),然后聲明一些抽象方法來迫使子類實現(xiàn)剩余的邏輯。不同的子類可以以不同的方式實現(xiàn)這些抽象方法,從而對剩余的邏輯有不同的實現(xiàn)。先制定一個頂級邏輯框架,而將邏輯的細節(jié)留給具體的子類去實現(xiàn)。

            23、VISITOR—情人節(jié)到了,要給每個MM送一束鮮花和一張卡片,可是每個MM送的花都要針對她個人的特點,每張卡片也要根據(jù)個人的特點來挑,我一個人哪搞得清楚,還是找花店老板和禮品店老板做一下Visitor,讓花店老板根據(jù)MM的特點選一束花,讓禮品店老板也根據(jù)每個人特點選一張卡,這樣就輕松多了;

            訪問者模式:訪問者模式的目的是封裝一些施加于某種數(shù)據(jù)結(jié)構(gòu)元素之上的操作。一旦這些操作需要修改的話,接受這個操作的數(shù)據(jù)結(jié)構(gòu)可以保持不變。訪問者模式適用于數(shù)據(jù)結(jié)構(gòu)相對未定的系統(tǒng),它把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作之間的耦合解脫開,使得操作集合可以相對自由的演化。訪問者模式使得增加新的操作變的很容易,就是增加一個新的訪問者類。訪問者模式將有關(guān)的行為集中到一個訪問者對象中,而不是分散到一個個的節(jié)點類中。當(dāng)使用訪問者模式時,要將盡可能多的對象瀏覽邏輯放在訪問者類中,而不是放到它的子類中。訪問者模式可以跨過幾個類的等級結(jié)構(gòu)訪問屬于不同的等級結(jié)構(gòu)的成員類。

          posted @ 2012-05-29 09:31 順其自然EVO 閱讀(125) | 評論 (0)編輯 收藏

          往返讀取后臺數(shù)據(jù)的代價

          數(shù)據(jù)庫最 重要是的為前臺應(yīng)用服務(wù)。 在眾多決定應(yīng)用性能的因素中, 如何快速有效從后臺讀取數(shù)據(jù)很大程度上地影響到最終效果。本文將對不同的數(shù)據(jù)往返(round-trip)讀取進行比較和歸納總結(jié)。最后的結(jié)果非常出人意 料。往往在時間緊迫的情況下,我們會本能地使用最簡單的方法來完成任務(wù),但是這種編譯習(xí)慣會讓我們的前臺應(yīng)用的性能大打折扣。

            返回 15,000 條數(shù)據(jù):這個測試會從一個表格里面讀取15000條數(shù)據(jù)。我們通過用三種不同的編譯方式來看如何提高數(shù)據(jù)庫提取的效率。

             以下這個腳本用來創(chuàng)建表格然后放入一百萬條數(shù)據(jù)。因為我們需要足夠多的數(shù)據(jù)來完成3個測試,每個測試讀取新鮮的數(shù)據(jù),所以創(chuàng)建了一百萬條。我創(chuàng)建的這個 列表每15000條數(shù)據(jù)一小組,這樣確保了測試讀取15000條數(shù)據(jù)的準(zhǔn)確性。不會因為數(shù)據(jù)的不同,而影響測試的結(jié)果。

            這個腳本稍作修改就可以放在MS SQL服務(wù)器上跑:

          createtabletest000 (
              intpkintprimarykey
             ,fillerchar(40)
          )
            
          --  BLOCK 1, first 5000 rows
          --  pgAdmin3: run as pgScript
          --  All others: modify as required
          --
          declare@x,@y;
          set@x = 1;
          set@y = string(40,40,1);
          while @x <= 5000begin
              insertintotest000 (intpk,filler)
              values((@x-1)*200 +1,'@y');
            
              set@x = @x + 1;
          end
            
          -- BLOCK 2, put 5000 rows aside
          --
          select *intotest000_tempfromtest000
            
          -- BLOCK 3, Insert the 5000 rows 199 more
          --          times to get 1million altogether
          --  pgAdmin3: run as pgScript
          --  All others: modify as required
          --
          declare@x;
          set@x = 1;
          while @x <= 199begin
              insertintotest000 (intpk,filler)
              selectintpk+@x,fillerfromtest000_temp;
            
              set@x = @x + 1;
          end

            測試-:基本代碼

            最簡單的代碼就是通過一個直白的查詢語句跑15000次往返。

          # Make adatabaseconnection
          $dbConn = pg_connect("dbname=roundTrips user=postgres");
            
          # Program 1, Individual explicit fetches
          $x1 = rand(0,199)*5000 + 1;
          $x2 = $x1 + 14999;
          echo"\nTest 1, using $x1 to $x2";
          $timeBegin = microtime(true);
          while ($x1++ <= $x2) {
              $dbResult = pg_exec("select * from test000 where intpk=$x1");
              $row = pg_fetch_array($dbResult);
          }
          $elapsed = microtime(true)-$timeBegin;
          echo"\nTest 1, elapsed time: ".$elapsed;
          echo"\n";
           測試二:準(zhǔn)備語句(Prepared Statement)

            這個代碼通過在循環(huán)前做一個準(zhǔn)備語句,雖然還是跑15000個往返,但是每次只是變化準(zhǔn)備語句的參數(shù)。

          # Make a database connection
          $dbConn = pg_connect("dbname=roundTrips user=postgres");
            
          # Program 2, Individual fetches with prepared statements
          $x1 = rand(0,199)*5000 + 1;
          $x2 = $x1 + 14999;
          echo "\nTest 2, using $x1 to $x2";
          $timeBegin = microtime(true);
          $dbResult = pg_prepare("test000","select * from test000 where intpk=$1");
          while ($x1++ <= $x2) {
              $pqResult = pg_execute("test000",array($x1));
              $row = pg_fetch_all($pqResult);
          }
          $elapsed = microtime(true)-$timeBegin;
          echo "\nTest 2, elapsed time: ".$elapsed;
          echo "\n";

            測試三:一個往返

            我們準(zhǔn)備一個語句命令去拿到所有15000條數(shù)據(jù),然后把他們一次返回過來。

          # Make a database connection
          $dbConn = pg_connect("dbname=roundTrips user=postgres");
            
          # Program 3, One fetch, pull all rows
          $timeBegin = microtime(true);
          $x1 = rand(0,199)*5000 + 1;
          $x2 = $x1 + 14999;
          echo "\nTest 3, using $x1 to $x2";
          $dbResult = pg_exec(
              "select * from test000 where intpk between $x1 and $x2"
          );
          $allRows = pg_fetch_all($dbResult);
          $elapsed = microtime(true)-$timeBegin;
          echo "\nTest 3, elapsed time: ".$elapsed;
          echo "\n";

            結(jié)果

            一共跑了5次,平均結(jié)果如下

            基本                  準(zhǔn)備              一次往返

            ~1.800 秒   ~1.150 秒    ~0.045 秒

            相比基本代碼,最后一個一次往返的邏輯快了大概40倍,比用準(zhǔn)備語句快了25倍左右。

            服務(wù)器和語言是否會影響性能呢?

            這個測試是在PHP/PostgresSQL上做的。其他語言和服務(wù)器上會不會得到不同的結(jié)果呢?如果是同樣的硬件,有可能這個數(shù)據(jù)絕對值會有所差異,但是相對的差距應(yīng)該是差不多。從一個往返里面讀取所有要索引的數(shù)據(jù)條比人和多次往返的語句都要快。

            活用活學(xué)

            這次測試最顯而易見的結(jié)論就是任何多于一條數(shù)據(jù)的索引都應(yīng)該使用這個方法。實際上,我們應(yīng)該把這個設(shè)置為默認語法,除非有絕好的理由。那么有哪些好理由呢?

            我跟我們的程序員聊過,有一位同學(xué)說:“你看,我們的應(yīng)用每次都是只要20-100個數(shù)據(jù)。絕對不會多了。我實在想 象不出20-100個數(shù)據(jù)的讀取值得翻新所有代碼。”所以我聽了以后,又去試了一下,實際上是這個方法確實只有100以上的才能看見顯著區(qū)別。在20的時 候,幾乎沒有區(qū)別。到了100, 一次往返的比基本的快6倍,比第二種方法快4倍。所以,使用與否的判斷在于個人。

            但是這里還有一個要考慮的因素是有多少同時進行的讀取在進行。如果你的系統(tǒng)是基于實時的設(shè)計,那么就有可能是不同的情況。我們這個測試是基于一個用戶,如果是多個用戶同時讀取,這種用戶行為會帶給數(shù)據(jù)庫一些額外的負擔(dān),我們需要一個更加宏觀的環(huán)境來比較。

            還有一個反對的聲音有可能是“我們是從不同的表格里面讀取數(shù)據(jù)。我們在某些線程上我們走一條條的,需要從不同的表格里面一條條讀取。”如果是這樣的話,你絕對需要使用一次往返,用幾個JOIN一次拿到。如果一個表格,都是慢10倍,幾個就慢好幾十倍了。

          posted @ 2012-05-28 09:27 順其自然EVO 閱讀(234) | 評論 (0)編輯 收藏

          Java類與對象的初始化

           面試的時候,經(jīng)常會遇到這樣的筆試題:給你兩個類的代碼,它們之間是繼承的關(guān)系,每個類里只有構(gòu)造器方法和靜態(tài)塊,它們只包含一些簡單的輸出字符串到控制臺的代碼,然后讓我們寫出正確的輸出結(jié)果。這實際上是在考察我們對于類的初始化知識的了解。

            首先,我們先看看下面的代碼,這就是很經(jīng)典的考察方式。

          1. public class InitField {  
          2.     public static void main(String[] args) {  
          3.         SuperInitField p = new SuperInitField();  
          4.         SuperInitField c = new SubInitField();  
          5.     }  
          6. }  

          7. class SuperInitField {  
          8.     public SuperInitField() {  
          9.         System.out.println("parent");  
          10.     }  
          11.     static {  
          12.         System.out.println("static parent");  
          13.     }  

          14. }  

          15. class SubInitField extends SuperInitField {  
          16.     public SubInitField() {  
          17.         System.out.println("child");  
          18.     }  
          19.     static {  
          20.         System.out.println("static child");  
          21.     }  
          22. }

            不管你是否能很快速的寫出正確的答案,我們先把這個程序放一邊,了解一下Java虛擬機初始化的原理。

            JVM通過加裝、連接和初始化一個Java類型,使該類型可以被正在運行的Java程序所使用。類型的生命周期如下圖所示:

            裝載和連接必須在初始化之前就要完成。

            類初始化階段,主要是為類變量賦予正確的初始值。這里的“正確”初始值指的是程序員希望這個類變量所具備的起始值。一個正確的初始值是通過類變量初始化語句或者靜態(tài)初始化語句給出的。初始化一個類包含兩個步驟:

            1)如果類存在直接超類的話,且直接超類還沒有被初始化,就先初始化直接超類。

            2)如果類存在一個類初始化方法,就執(zhí)行此方法。

            那什么時候類會進行初始化呢?Java 虛擬機規(guī)范為類的初始化時機做了嚴(yán)格定義:在首次主動使用時初始化。

           那哪些情形才符合首次主動使用的標(biāo)準(zhǔn)呢?Java虛擬機規(guī)范對此作出了說明,他們分別是:

            1)創(chuàng)建類的新實例;

            2)調(diào)用類的靜態(tài)方法;

            3)操作類或接口的靜態(tài)字段(final字段除外);

            4)調(diào)用Java的特定的反射方法;

            5)初始化一個類的子類;

            6)指定一個類作為Java虛擬機啟動時的初始化類。

            除了以上六種情形以外,所有其它的方式都是被動使用的,不會導(dǎo)致類的初始化。

            一旦一個類被裝載、連接和初始化,它就隨時可以使用了。現(xiàn)在我們來關(guān)注對象的實例化,對象實例化和初始化是就是對象生命的起始階段的活動。

            Java編譯器為它編譯的每個類都至少生成一個實例初始化方法,即<init>()方法。源代碼中的每一個類的構(gòu)造方法都有一個相 對應(yīng)的<init>()方法。如果類沒有明確地聲明任何構(gòu)造方法,編譯器則為該類生成一個默認的無參構(gòu)造方法,這個默認的構(gòu)造器僅僅調(diào)用父類 的無參構(gòu)造器。

            一個<init>()方法內(nèi)包括的代碼內(nèi)容可能有三種:調(diào)用另一個<init>() 方法;對實例變量初始化;構(gòu)造方法體的代碼。

            如果構(gòu)造方法是明確地從調(diào)用同一個類中的另一個構(gòu)造方法開始,那它對應(yīng)的 <init>() 方法體內(nèi)包括的內(nèi)容為:

            1、一個對本類的<init>()方法的調(diào)用;

            2、實現(xiàn)了對應(yīng)構(gòu)造方法的方法體的字節(jié)碼。

            如果構(gòu)造方法不是通過調(diào)用自身類的其它構(gòu)造方法開始,并且該對象不是 Object 對象,那 <init>() 法內(nèi)則包括的內(nèi)容為:

            1、一個父類的<init>()方法的調(diào)用;

            2、任意實例變量初始化方法的字節(jié)碼;

            3、實現(xiàn)了對應(yīng)構(gòu)造方法的方法體的字節(jié)碼。

            通過上面的講解是不是對你理解Java類型的初始化有一定的幫助呢?

            好,那我們再來分析一下開始的那段代碼:

          1. SuperInitField p = new SuperInitField();  
          2. //SuperInitField的超類是Object  
          3. //創(chuàng)建SuperInitField對象,屬于首次主動使用,因此要先初始化Object類,然后再調(diào)用SuperInitField類變量初始化語句或者靜態(tài)初始化語句,所以要輸出static parent  
          4. //類被裝載、連接和初始化之后,創(chuàng)建一個對象,因此需要首先調(diào)用了Object的默認構(gòu)造方法,然后再調(diào)用自己的構(gòu)造方法,所以要輸出parent  
          5.  
          6. SuperInitField c = new SubInitField();  
          7. //SubInitField繼承自SuperInitField  
          8. //創(chuàng)建SubInitField對象,屬于首次主動使用,父類SuperInitField已被初始化,因此只要調(diào)用SubInitField類變量初始化語句或者靜態(tài)初始化語句,所以要輸出static child  
          9. //類被裝載、連接和初始化之后,創(chuàng)建一個對象,因此需要首先調(diào)用了SuperInitField的構(gòu)造方法,然后再調(diào)用自己的構(gòu)造方法,所以要輸出parent,然后再輸出child

            到現(xiàn)在你應(yīng)該大體了解了Java類初始化的原理了吧,那我就留一到練習(xí)題吧,寫出下列代碼的運行結(jié)果。

          1. public class Test {  
          2.     public Test(){  
          3.         System.out.println("parent");  
          4.     }  
          5.     static{  
          6.         System.out.println("static parent");  
          7.     }  
          8.     public static void main(String[] args) {  
          9.         System.out.println("main");  
          10.     }  
          11. }

            這道題是關(guān)于初始化順序的,已經(jīng)有人寫過這方面的文章了,我就不多說了。

          posted @ 2012-05-28 09:25 順其自然EVO 閱讀(190) | 評論 (0)編輯 收藏

          Java實例化類的方法

           Java中,類的實例化方法有四種途徑:

            1)使用new操作符

            2)調(diào)用Class對象的newInstance()方法

            3)調(diào)用clone()方法,對現(xiàn)有實例的拷貝

            4)通過ObjectInputStream的readObject()方法反序列化類

            1、ClassInstance.java

          1. import java.io.*;  
          2.    
          3.  class ClassInstance implements Cloneable, Serializable {  
          4.      private String str = "測試...";  
          5.      public void fun(){  
          6.          System.out.println(str);  
          7.      }  
          8.      public ClassInstance(String str){  
          9.          System.out.println("有參類的實例化");  
          10.          this.str += str;   
          11.      }  
          12.      public ClassInstance(){  
          13.          System.out.println("無參類的實例化");  
          14.      }  
          15.      public Object clone(){  
          16.          return this;  
          17.      }  
          18.  }

            2、ClassInstanceTest.java

          1. import java.io.*;  
          2.  import java.lang.reflect.*;  
          3.    
          4.  public class ClassInstanceTest{  
          5.      public static void main(String[] args) throws ClassNotFoundException, InstantiationException,  
          6.             IllegalAccessException, IOException,InvocationTargetException, NoSuchMethodException{  
          7.          //第一種類的實例化方式 
          8.          ClassInstance ci01 = new ClassInstance("01");  
          9.          ci01.fun();  
          10.    
          11.          //第二種類的實例化方式 
          12.          ClassInstance ci02 = (ClassInstance) Class.forName("ClassInstance").newInstance();  
          13.          ci02.fun();  
          14.    
          15.          //第三種類的實例化方式 
          16.          ClassInstance ci03 = (ClassInstance) ci01.clone();  
          17.          ci03.fun();  
          18.    
          19.          //第四種類的實例化方式 
          20.          FileOutputStream fos = new FileOutputStream("ci.tmp");  
          21.          ObjectOutputStream oos = new ObjectOutputStream(fos);  
          22.          oos.writeObject(ci01);  
          23.          oos.close();  
          24.          fos.close();  
          25.            
          26.          FileInputStream fis = new FileInputStream("ci.tmp");  
          27.          ObjectInputStream ois = new ObjectInputStream(fis);  
          28.            
          29.          ClassInstance ci04  = (ClassInstance) ois.readObject();  
          30.          ois.close();  
          31.          fis.close();  
          32.            
          33.          ci04.fun();  
          34.          System.out.println("--------------------額外測試--------------------");  
          35.          ClassInstance ci05 = null;  
          36.          //額外的思考 在第二種類實例化的方式中有沒有一種方法實現(xiàn)有參數(shù)的構(gòu)造方式 
          37.          //獲得類的構(gòu)造信息 
          38.          Constructor[] ctor = Class.forName("ClassInstance").getDeclaredConstructors();  
          39.          //找到我們需要的構(gòu)造方法 
          40.          for(int i=0;i<ctor.length;i++ ){  
          41.              Class[] cl = ctor[i].getParameterTypes();  
          42.              if(cl.length == 1){  
          43.                  //實例化對象 
          44.                  ci05 = (ClassInstance) Class.forName("ClassInstance").getConstructor(cl).newInstance(new Object[]{"05"});  
          45.              }  
          46.          }  
          47.          ci05.fun();  
          48.      }  
          49.  }

            3、輸出結(jié)果

          1. 有參類的實例化  
          2.  測試...01  
          3.  無參類的實例化  
          4.  測試...  
          5.  測試...01  
          6.  測試...01  
          7.  -------------------額外測試--------------------  
          8.  有參類的實例化  
          9.  測試...05

            除了這幾種情況可以實例化一個Java類對象外,隱式調(diào)用實例化也是利用了已上集中情況。例如常見的方法:

          1. public class ClassInstance{  
          2.  public ClassInstance(){  
          3.  }  
          4.      public ClassInstance getInstance(){  
          5.  return new ClassInstance();  
          6.  }  
          7.  }

            通過觀察結(jié)果,我們發(fā)現(xiàn)無論哪一種方式必須經(jīng)過的一步---調(diào)用構(gòu)造方法。無論怎樣構(gòu)造函數(shù)作為初始化類的意義怎樣都不會改變。

          posted @ 2012-05-25 10:15 順其自然EVO 閱讀(225) | 評論 (0)編輯 收藏

          關(guān)于Java自增操作的原子性

           最近在工作中和一個同事因為自增是不是原子性操作爭論的面紅耳赤,那Java的自增操作到底是不是原子性操作呢,答案是否的,即Java的自增操作不是原子性操作。

            1、首先我們先看看Bruce Eckel是怎么說的:

            In the JVM an increment is not atomic and involves both a read and a write. (via the latest Java Performance Tuning Newsletter)

            意思很簡單,就是說在jvm中自增不是原子性操作,它包含一個讀操作和一個寫操作。

            2、以上可能還不能讓你信服,要想讓人心服口服,就必須用代碼說話。正如FaceBook的文化一樣:代碼贏得爭論。那我們就看一段代碼:

            以下的代碼是用100個線程同時執(zhí)行自增操作,每個線程自增100次,如果自增操作是原子性操作的話,那么執(zhí)行完amount的值為10,000。運行代碼之后,你會發(fā)現(xiàn)amount的值小于10,000,這就說明自增操作不是原子性的

          1. /** 
          2.  *  
          3.  * @author renrun.wu 
          4.  */ 
          5. public class MultiThread implements Runnable {  
          6.     private int count;  
          7.     private int amount = 1;  
          8.       
          9.     public MultiThread() {  
          10.          count = 100;  
          11.     }  
          12.       
          13.     public MultiThread(int count) {  
          14.         this.count = count;  
          15.     }  
          16.       
          17.     @Override 
          18.     public void run() {  
          19.         for (int i = 0; i < count; i++) {  
          20.             amount++;  
          21.         }  
          22.     }  
          23.       
          24.     public static void main(String[] args) {  
          25.         ExecutorService executorService = Executors.newCachedThreadPool();  
          26.         MultiThread multiThread =new MultiThread();  
          27.         for (int i = 0; i < 100; i++) {  
          28.             executorService.execute(multiThread);  
          29.         }  
          30.         executorService.shutdown();  
          31.           
          32.         try {  
          33.             Thread.sleep(60000);  
          34.         } catch (InterruptedException e) {  
          35.             e.printStackTrace();  
          36.         }  
          37.         System.out.println(multiThread.amount);  
          38.     }  
          39. }

            3、如果以上還不能讓你信服的話,也沒關(guān)系。我們就把自增操作反編譯出來,看看java字節(jié)碼是怎么操作的

            以下是一個簡單的自增操作代碼

          1. public class Increment {  
          2.     private int id = 0;  
          3.     public void getNext(){  
          4.         id++;  
          5.     }  
          6. }


           我們看看反編譯之后的Java字節(jié)碼,主要關(guān)注getNext()方法內(nèi)部的Java字節(jié)碼。

          1. public class Increment extends java.lang.Object{  
          2.     public Increment();  
          3.       Code:  
          4. :   aload_0  
          5. :   invokespecial   #1//Method java/lang/Object."<init>":()V  
          6. :   aload_0  
          7. :   iconst_0  
          8. :   putfield        #2//Field id:I  
          9. :   return 
          10.  
          11.     public void getNext();  
          12.       Code:  
          13. :   aload_0   //加載局部變量表index為0的變量,在這里是this   
          14. :   dup                 //將當(dāng)前棧頂?shù)膶ο笠脧?fù)制一份  
          15. :   getfield        #2//Field id:I,獲取id的值,并將其值壓入棧頂  
          16. :   iconst_1            //將int型的值1壓入棧頂  
          17. :   iadd                //將棧頂兩個int類型的元素相加,并將其值壓入棧頂  
          18. :   putfield        #2//Field id:I,將棧頂?shù)闹蒂x值給id  
          19. :  return 
          20.  
          21.     }

            很明顯,我們能夠看到在getNext()方法內(nèi)部,對于類變量id有一個先取值后加一再賦值的過程。因此,我們可以很肯定的說Java中的自增操作不是原子性的。

            4、也許你會問,那局部變量的自增操作是否是原子性的。好,我們在看看一下代碼:

          1. public class Increment {  
          2.     public void getNext(){  
          3.     int id = 0;  
          4.         id++;  
          5.     }  
          6. }

            我們再看看反編譯之后的Java字節(jié)碼,主要還是關(guān)注getNext()方法內(nèi)部的Java字節(jié)碼。

          1. public class Increment extends java.lang.Object{  
          2. public Increment();  
          3.   Code:  
          4. :   aload_0  
          5. :   invokespecial   #1//Method java/lang/Object."<init>":()V  
          6. :   return 
          7.  
          8. public void getNext();  
          9.   Code:  
          10. :   iconst_0  
          11. :   istore_1  
          12. :   iinc    11 
          13. :   return 
          14.  
          15. }

            與全局變量的自增操作相比,很明顯局部變量的自增操作少了getfield與putfield操作。而且對于局部變量來說,它無論如何都不會涉及到多線程的操作,因此局部變量的自增操作是否是原子操作也就顯得不那么重要了。

          posted @ 2012-05-25 10:12 順其自然EVO 閱讀(192) | 評論 (0)編輯 收藏

          僅列出標(biāo)題
          共394頁: First 上一頁 322 323 324 325 326 327 328 329 330 下一頁 Last 
          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 永清县| 沙湾县| 福泉市| 海兴县| 定结县| 治县。| 道真| 梅州市| 加查县| 家居| 宁津县| 林芝县| 平安县| 凤庆县| 临海市| 沙洋县| 宜黄县| 铜鼓县| 霸州市| 石泉县| 隆安县| 林甸县| 龙江县| 阜新| 涪陵区| 桦南县| 盐津县| 黄浦区| 布尔津县| 郧西县| 任丘市| 江陵县| 子长县| 阳原县| 嵊泗县| 来凤县| 治县。| 江安县| 乌兰浩特市| 库尔勒市| 宁阳县|