posts - 12, comments - 8, trackbacks - 0, articles - 5
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          2020年4月18日

          博客做了遷移,新的地址:觀云的BLOG https://yeas.fun/
          新博課主要用于記錄一些系列的技術(shù)文章,今年的一個目標(biāo)就是深入研究JVM底層,我會把一些學(xué)習(xí)心得記錄下來,也歡迎大家一起討論~

          posted @ 2020-04-18 10:12 楊羅羅 閱讀(216) | 評論 (0)編輯 收藏

          2016年1月13日

          現(xiàn)在網(wǎng)絡(luò)那么發(fā)達(dá),我們上網(wǎng)的每個人勢必會在各個網(wǎng)站上登陸,那勢必會有一堆密碼需要管理,那怎么能記住那么多網(wǎng)站的密碼呢?我之前的做法是設(shè)置幾個常用的密碼,好多不重要的網(wǎng)站用一個,重要的網(wǎng)站用一個,然后...CSDN密碼泄露之后,只得吭哧吭哧的改一堆密碼。那種痛苦真的是呵呵呵

          那有沒有什么方式可以方便的管理密碼呢?

          那就是LastPass的工作,它是一款跨平臺密碼管理軟件。你在每個網(wǎng)站上都可以隨機(jī)生成一個密碼,然后軟件會自動記住,你只需要記住這個軟件的主密碼就可以了。超方便!

          為什么要用LastPass?

          我用LastPass,是因為它可以安裝瀏覽器插件,之后你在網(wǎng)站的注冊,它會自動提醒你要不要加入它的密碼庫,你在網(wǎng)站的登陸它會自動幫你填寫賬號密碼,甚至于一些常用的表單(比如說身份證、公司地址、銀行卡等)你可以提前設(shè)置好,它也會自動幫你填寫,我再也不用記那么多密碼了,一切都是自動化。
          如果你經(jīng)常有國外的帳號,比如說google等大公司,LastPass甚至提供了一鍵改密碼的功能,好方便!

          支持智能手機(jī)么?

          當(dāng)然!現(xiàn)在智能機(jī)那么流行,不能在智能機(jī)上用,簡直不能忍。當(dāng)你安裝了IOS軟件之后,在打開網(wǎng)站登錄界面,點擊下方的向上小箭頭,選擇LassPass軟件,點擊Touch ID,好了,密碼自動完成,世界頓時清凈了,想想以前在手機(jī)上輸入超長的密碼,跪了!

          真的安全嗎?

          有的人擔(dān)心密碼泄露問題,其實對于LastPass沒啥必要,因為LastPass存儲的都是加密文件,只要你的主密碼不泄露,別人即使拿到你的網(wǎng)上的密碼,也是加密的,沒法用。

          它收費嗎?

          好東西都要收費,價格也還可以,幾十塊一年,其實收費版和免費版對于普通用戶,最重要的區(qū)別就是:免費版帳號密碼不能云同步

          免費獲得一個月的高級賬戶權(quán)限

          https://lastpass.com/f?18430702  通過這個地址注冊,則會免費獲得一個月高級賬戶權(quán)限

          最后也是最重要的:

          點擊這個鏈接,輸入剛剛你注冊的郵箱,則會送半年的高級賬戶,記住一個密碼,就記住了所有密碼,就是那么簡單!

          posted @ 2016-01-13 14:19 楊羅羅 閱讀(347) | 評論 (0)編輯 收藏

          2011年4月6日

          一. 應(yīng)用場景

          在大型分布式應(yīng)用中,我們經(jīng)常碰到在多數(shù)據(jù)庫之間的數(shù)據(jù)同步問題,比如說一款游戲,在玩家注冊后,可以馬上登陸進(jìn)入服務(wù)器,數(shù)據(jù)在一個IDC更新,其它IDC立即可見。為了簡化思路,我們這里稱玩家注冊的數(shù)據(jù)庫(數(shù)據(jù)來源庫)為中心庫,同步目的地的數(shù)據(jù)庫為分站庫。

          在分布式領(lǐng)域有個CAP理論,是說Consistency(一致性), Availability(可用性), Partition tolerance(分區(qū)和容錯) 三部分在系統(tǒng)實現(xiàn)只可同時滿足二點,法三者兼顧。

          能做的

          · 數(shù)據(jù)快速搬運(yùn)到指定的IDC節(jié)點

          · 數(shù)據(jù)傳遞過程中失敗時,重新傳遞

          · 監(jiān)控數(shù)據(jù)傳遞流程

          · 故障轉(zhuǎn)移

          · 數(shù)據(jù)版本控制

          · 分配全局唯一的ID

          不能做的

          · 不參與業(yè)務(wù)行為,業(yè)務(wù)操作只能通過注冊的方式集成

          · 不保存業(yè)務(wù)數(shù)據(jù),不提供傳遞的業(yè)務(wù)的查詢

          二.系統(tǒng)要求

          1.數(shù)據(jù)快速同步:除去網(wǎng)絡(luò)原因,正常情況下從來源庫同步到接收庫的時間不超過300m2.高并發(fā):單個應(yīng)用每秒同步2000條記錄
          3.可伸縮性,在資源達(dá)到上限時能通過增加應(yīng)用分散處理后期增長的壓力
          4.數(shù)據(jù)完整性要求,在數(shù)據(jù)同步過程中保證數(shù)據(jù)不丟失和數(shù)據(jù)安全
          5.故障轉(zhuǎn)移和數(shù)據(jù)恢復(fù)功能

          三.設(shè)計思路

          系統(tǒng)優(yōu)化,最常用的就是進(jìn)行業(yè)務(wù)切割,將總?cè)蝿?wù)切割為許多子任務(wù),分區(qū)塊分析系統(tǒng)中可能存在的性能瓶頸并有針對性地進(jìn)行優(yōu)化,在本系統(tǒng)中,主要業(yè)務(wù)包含以下內(nèi)容:
          1.Syncer:外部接口,接收同步數(shù)據(jù)請求,初始化同步系統(tǒng)的一些必要數(shù)據(jù)
          2.Delivery:將同步數(shù)據(jù)按照業(yè)務(wù)或優(yōu)先級進(jìn)行分發(fā),并記錄分發(fā)結(jié)果
          3.Batch:分站庫收到同步數(shù)據(jù)后,根據(jù)不同的業(yè)務(wù)類型調(diào)用相應(yīng)的業(yè)務(wù)邏輯處理數(shù)據(jù)
          基于以上三塊業(yè)務(wù)功能,我們可以把整個數(shù)據(jù)同步流程切割為3個應(yīng)用,具體如下圖顯示。在Syncer端應(yīng)用中,我們需要將原始同步數(shù)據(jù)和分發(fā)的分站進(jìn)行存儲,以備失敗恢復(fù),此時如果采用數(shù)據(jù)庫進(jìn)行存儲,勢必會受限于數(shù)據(jù)庫性能影響,因此我們采用了高效的key-value風(fēng)格存儲的redis服務(wù)來記錄數(shù)據(jù),同時在不同應(yīng)用之間采用隊列(Httpsqs服務(wù))的方式來進(jìn)行通訊,同時也保證的數(shù)據(jù)通訊的順序性,為之后的順序同步做好基礎(chǔ)。
          Httpsqs提供了http風(fēng)格的數(shù)據(jù)操作模式,業(yè)務(wù)編碼非常簡單,同時也提供了web形式的隊列處理情況查詢,這是選擇它做隊列服務(wù)很大一部分原因:方便業(yè)務(wù)操作和性能監(jiān)控。

          四.數(shù)據(jù)流轉(zhuǎn) 

          綠色-正常流程、紅色-異常流程

          隊列處理

          根據(jù)業(yè)務(wù)劃分隊列名稱,每個隊列再劃分為三個關(guān)聯(lián)隊列:正常隊列(Normal)、重試隊列(Retry)、死亡隊列(Death),處理流程為:

          【進(jìn)程A】把數(shù)據(jù)先放入正常隊列,如果放置失敗寫恢復(fù)日志

          【進(jìn)程B】監(jiān)聽正常隊列,獲取隊列數(shù)據(jù)并進(jìn)行業(yè)務(wù)處理,處理失敗放入重試隊列

          【進(jìn)程C】監(jiān)聽重試隊列,過幾秒獲取隊列數(shù)據(jù)并重新進(jìn)行業(yè)務(wù)處理,處理失敗放入死亡隊列

          【進(jìn)程D】監(jiān)聽死亡隊列,獲取隊列數(shù)據(jù)并重新進(jìn)行業(yè)務(wù)處理,處理失敗重新放入死亡隊列尾部,等待下一次輪回

          業(yè)務(wù)處理失敗如果無法再次放入隊列,記錄恢復(fù)日志

          數(shù)據(jù)同步流程

          1發(fā)送數(shù)據(jù),支持Http POST:curl -d "經(jīng)過URL編碼的文本消息",如"http://host:port/sync_all/register"
          或者Http GET:curl "http://host:port/sync_all/register?data=經(jīng)過URL編碼的文本消息"

          sync-syncer接收到同步數(shù)據(jù)請求,創(chuàng)建sid并分解出需要同步的節(jié)點個數(shù),把原始數(shù)據(jù)和子任務(wù)寫入redis中,sid寫入httpsqs中

          sync-delivery監(jiān)聽中心httpsqs隊列,根據(jù)sid從redis獲取到原始數(shù)據(jù)和需要同步的節(jié)點地址,往其他節(jié)點發(fā)送數(shù)據(jù),流程如按"隊列處理流程"進(jìn)行

          sync-batch監(jiān)聽分節(jié)點的httpsqs隊列,調(diào)用已經(jīng)注冊的處理器處理隊列數(shù)據(jù),流程如按"隊列處理流程"進(jìn)行

          三. 恢復(fù)和監(jiān)控

          恢復(fù)數(shù)據(jù)源

          · httpsqs中的死亡隊列 - 業(yè)務(wù)處理暫時處理不了的數(shù)據(jù)

          · recovery日志文件 - 其它異常情況下的數(shù)據(jù),例如網(wǎng)絡(luò)無法連接、內(nèi)部服務(wù)不可用

          數(shù)據(jù)恢復(fù)

          獨立的應(yīng)用來處理正常流程中沒有完成的任務(wù),主要功能有:

          · 監(jiān)聽死亡隊列,進(jìn)行業(yè)務(wù)重做,再次執(zhí)行失敗時將執(zhí)行次數(shù)+1,最大執(zhí)行次數(shù)為5(默認(rèn)),超出上限則記錄到恢復(fù)日志中

          · 讀取恢復(fù)日志,重新放入死亡隊列

          應(yīng)用監(jiān)控

          · 使用scribe日志框架服務(wù)業(yè)務(wù)日志的采集和監(jiān)控

          · 收集重要的業(yè)務(wù)操作日志

          · 動態(tài)的開啟/關(guān)閉某類業(yè)務(wù)日志

          · 對redis進(jìn)行監(jiān)控

          · 對httpsps,監(jiān)控隊列個數(shù),每個隊列的狀態(tài)

          四. 數(shù)據(jù)結(jié)構(gòu)

          {"sid":111,"type":"reg","v":1,"data":"hello world","ctime":65711321800,"exec":1}

          · sid(sync id) - 全局唯一id

          · v(version) - 版本號

          · data - 業(yè)務(wù)數(shù)據(jù)

          · ctime(create time) - 創(chuàng)建時間(毫秒)

          · exec - 可選,執(zhí)行次數(shù)

          類別

          key格式

          value格式

          備注

          redis原始數(shù)據(jù)

          sync:<業(yè)務(wù)類型>:<sid>

          {"ctime":65711321800,"v":1,"data":"hello world"}

          分站沒有此項

          redis業(yè)務(wù)附加任務(wù)

          sync:<業(yè)務(wù)類型>:<sid>:sub

          set類型,保存需要同步的節(jié)點id,例如[1,3,5]

          分發(fā)確認(rèn)Set數(shù)據(jù)結(jié)構(gòu) 

          httpsqs隊列

          sync:<業(yè)務(wù)類型> 
          sync:<業(yè)務(wù)類型>:retry 
          sync:<業(yè)務(wù)類型>:death

          {"sid":111,"type":"pp_register","exec":1} 

          中心隊列內(nèi)容,key中<業(yè)務(wù)類型>是可選項 

          httpsqs隊列

          sync:<業(yè)務(wù)類型> 
          sync:<業(yè)務(wù)類型>:retry 
          sync:<業(yè)務(wù)類型>:death

          {"sid":111,"v":1,"data":"hello world","ctime":65711321800,"exec":1} 

          分站隊列內(nèi)容,包含業(yè)務(wù)數(shù)據(jù) 

          所有的key都小寫,以 ':' 作為分隔符

          五.編碼及測試結(jié)果

          經(jīng)過編碼和測試,在內(nèi)網(wǎng)環(huán)境下,在無數(shù)據(jù)庫限制的情況下,單應(yīng)用可以傳遞1500條/秒,基本滿足業(yè)務(wù)需求。如果需進(jìn)一步擴(kuò)展,采用集群式布署可使得吞吐量成倍的增長。

          posted @ 2011-04-06 15:50 楊羅羅 閱讀(3630) | 評論 (3)編輯 收藏

          2010年12月15日

          下面這篇文章寫的非常好,結(jié)合memcached的 特點利用Consistent hasning 算法,可以打造一個非常完備的分布式緩存服務(wù)器。

          memcached的分布式

          正如第1次中介紹的那樣, memcached雖然稱為“分布式”緩存服務(wù)器,但服務(wù)器端并沒有“分布式”功能。 服務(wù)器端僅包括 第2次、 第3次 前坂介紹的內(nèi)存存儲功能,其實現(xiàn)非常簡單。 至于memcached的分布式,則是完全由客戶端程序庫實現(xiàn)的。 這種分布式是memcached的最大特點。

          memcached的分布式是什么意思?

          這里多次使用了“分布式”這個詞,但并未做詳細(xì)解釋。 現(xiàn)在開始簡單地介紹一下其原理,各個客戶端的實現(xiàn)基本相同。

          下面假設(shè)memcached服務(wù)器有node1~node3三臺, 應(yīng)用程序要保存鍵名為“tokyo”“kanagawa”“chiba”“saitama”“gunma” 的數(shù)據(jù)。

          memcached-0004-01.png

          圖1 分布式簡介:準(zhǔn)備

          首先向memcached中添加“tokyo”。將“tokyo”傳給客戶端程序庫后, 客戶端實現(xiàn)的算法就會根據(jù)“鍵”來決定保存數(shù)據(jù)的memcached服務(wù)器。 服務(wù)器選定后,即命令它保存“tokyo”及其值。

          memcached-0004-02.png

          圖2 分布式簡介:添加時

          同樣,“kanagawa”“chiba”“saitama”“gunma”都是先選擇服務(wù)器再保存。

          接下來獲取保存的數(shù)據(jù)。獲取時也要將要獲取的鍵“tokyo”傳遞給函數(shù)庫。 函數(shù)庫通過與數(shù)據(jù)保存時相同的算法,根據(jù)“鍵”選擇服務(wù)器。 使用的算法相同,就能選中與保存時相同的服務(wù)器,然后發(fā)送get命令。 只要數(shù)據(jù)沒有因為某些原因被刪除,就能獲得保存的值。

          memcached-0004-03.png

          圖3 分布式簡介:獲取時

          這樣,將不同的鍵保存到不同的服務(wù)器上,就實現(xiàn)了memcached的分布式。 memcached服務(wù)器增多后,鍵就會分散,即使一臺memcached服務(wù)器發(fā)生故障 無法連接,也不會影響其他的緩存,系統(tǒng)依然能繼續(xù)運(yùn)行。

          接下來介紹第1次 中提到的Perl客戶端函數(shù)庫Cache::Memcached實現(xiàn)的分布式方法。

          Cache::Memcached的分布式方法

          Perl的memcached客戶端函數(shù)庫Cache::Memcached是 memcached的作者Brad Fitzpatrick的作品,可以說是原裝的函數(shù)庫了。

          該函數(shù)庫實現(xiàn)了分布式功能,是memcached標(biāo)準(zhǔn)的分布式方法。

          根據(jù)余數(shù)計算分散

          Cache::Memcached的分布式方法簡單來說,就是“根據(jù)服務(wù)器臺數(shù)的余數(shù)進(jìn)行分散”。 求得鍵的整數(shù)哈希值,再除以服務(wù)器臺數(shù),根據(jù)其余數(shù)來選擇服務(wù)器。

          下面將Cache::Memcached簡化成以下的Perl腳本來進(jìn)行說明。

          use strict;
          use warnings;
          use String::CRC32;

          my @nodes = ('node1','node2','node3');
          my @keys = ('tokyo', 'kanagawa', 'chiba', 'saitama', 'gunma');

          foreach my $key (@keys) {
             
          my $crc = crc32($key); # CRC値
             
          my $mod = $crc % ( $#nodes + 1 );
             
          my $server = $nodes[ $mod ]; # 根據(jù)余數(shù)選擇服務(wù)器
             
          printf "%s =&gt; %s\n", $key, $server;
          }

          Cache::Memcached在求哈希值時使用了CRC。

          首先求得字符串的CRC值,根據(jù)該值除以服務(wù)器節(jié)點數(shù)目得到的余數(shù)決定服務(wù)器。 上面的代碼執(zhí)行后輸入以下結(jié)果:

          tokyo       => node2
          kanagawa => node3
          chiba => node2
          saitama => node1
          gunma => node1

          根據(jù)該結(jié)果,“tokyo”分散到node2,“kanagawa”分散到node3等。 多說一句,當(dāng)選擇的服務(wù)器無法連接時,Cache::Memcached會將連接次數(shù) 添加到鍵之后,再次計算哈希值并嘗試連接。這個動作稱為rehash。 不希望rehash時可以在生成Cache::Memcached對象時指定“rehash => 0”選項。

          根據(jù)余數(shù)計算分散的缺點

          余數(shù)計算的方法簡單,數(shù)據(jù)的分散性也相當(dāng)優(yōu)秀,但也有其缺點。 那就是當(dāng)添加或移除服務(wù)器時,緩存重組的代價相當(dāng)巨大。 添加服務(wù)器后,余數(shù)就會產(chǎn)生巨變,這樣就無法獲取與保存時相同的服務(wù)器, 從而影響緩存的命中率。用Perl寫段代碼來驗證其代價。

          use strict;
          use warnings;
          use String::CRC32;

          my @nodes = @ARGV;
          my @keys = ('a'..'z');
          my %nodes;

          foreach my $key ( @keys ) {
             
          my $hash = crc32($key);
             
          my $mod = $hash % ( $#nodes + 1 );
             
          my $server = $nodes[ $mod ];
             
          push @{ $nodes{ $server } }, $key;
          }

          foreach my $node ( sort keys %nodes ) {
             
          printf "%s: %s\n", $node, join ",", @{ $nodes{$node} };
          }

          這段Perl腳本演示了將“a”到“z”的鍵保存到memcached并訪問的情況。 將其保存為mod.pl并執(zhí)行。

          首先,當(dāng)服務(wù)器只有三臺時:

          $ mod.pl node1 node2 nod3
          node1: a,c,d,e,h,j,n,u,w,x
          node2: g,i,k,l,p,r,s,y
          node3: b,f,m,o,q,t,v,z

          結(jié)果如上,node1保存a、c、d、e……,node2保存g、i、k……, 每臺服務(wù)器都保存了8個到10個數(shù)據(jù)。

          接下來增加一臺memcached服務(wù)器。

          $ mod.pl node1 node2 node3 node4
          node1: d,f,m,o,t,v
          node2: b,i,k,p,r,y
          node3: e,g,l,n,u,w
          node4: a,c,h,j,q,s,x,z

          添加了node4。可見,只有d、i、k、p、r、y命中了。像這樣,添加節(jié)點后 鍵分散到的服務(wù)器會發(fā)生巨大變化。26個鍵中只有六個在訪問原來的服務(wù)器, 其他的全都移到了其他服務(wù)器。命中率降低到23%。在Web應(yīng)用程序中使用memcached時, 在添加memcached服務(wù)器的瞬間緩存效率會大幅度下降,負(fù)載會集中到數(shù)據(jù)庫服務(wù)器上, 有可能會發(fā)生無法提供正常服務(wù)的情況。

          mixi的Web應(yīng)用程序運(yùn)用中也有這個問題,導(dǎo)致無法添加memcached服務(wù)器。 但由于使用了新的分布式方法,現(xiàn)在可以輕而易舉地添加memcached服務(wù)器了。 這種分布式方法稱為 Consistent Hashing。

          Consistent Hashing

          關(guān)于Consistent Hashing的思想,mixi株式會社的開發(fā)blog等許多地方都介紹過, 這里只簡單地說明一下。

          Consistent Hashing的簡單說明

          Consistent Hashing如下所示:首先求出memcached服務(wù)器(節(jié)點)的哈希值, 并將其配置到0~232的圓(continuum)上。 然后用同樣的方法求出存儲數(shù)據(jù)的鍵的哈希值,并映射到圓上。 然后從數(shù)據(jù)映射到的位置開始順時針查找,將數(shù)據(jù)保存到找到的第一個服務(wù)器上。 如果超過232仍然找不到服務(wù)器,就會保存到第一臺memcached服務(wù)器上。

          memcached-0004-04.png

          圖4 Consistent Hashing:基本原理

          從上圖的狀態(tài)中添加一臺memcached服務(wù)器。余數(shù)分布式算法由于保存鍵的服務(wù)器會發(fā)生巨大變化 而影響緩存的命中率,但Consistent Hashing中,只有在continuum上增加服務(wù)器的地點逆時針方向的 第一臺服務(wù)器上的鍵會受到影響。

          memcached-0004-05.png

          圖5 Consistent Hashing:添加服務(wù)器

          因此,Consistent Hashing最大限度地抑制了鍵的重新分布。 而且,有的Consistent Hashing的實現(xiàn)方法還采用了虛擬節(jié)點的思想。 使用一般的hash函數(shù)的話,服務(wù)器的映射地點的分布非常不均勻。 因此,使用虛擬節(jié)點的思想,為每個物理節(jié)點(服務(wù)器) 在continuum上分配100~200個點。這樣就能抑制分布不均勻, 最大限度地減小服務(wù)器增減時的緩存重新分布。

          通過下文中介紹的使用Consistent Hashing算法的memcached客戶端函數(shù)庫進(jìn)行測試的結(jié)果是, 由服務(wù)器臺數(shù)(n)和增加的服務(wù)器臺數(shù)(m)計算增加服務(wù)器后的命中率計算公式如下:

          (1 - n/(n+m)) * 100

          支持Consistent Hashing的函數(shù)庫

          本連載中多次介紹的Cache::Memcached雖然不支持Consistent Hashing, 但已有幾個客戶端函數(shù)庫支持了這種新的分布式算法。 第一個支持Consistent Hashing和虛擬節(jié)點的memcached客戶端函數(shù)庫是 名為libketama的PHP庫,由last.fm開發(fā)。

          至于Perl客戶端,連載的第1次 中介紹過的Cache::Memcached::Fast和Cache::Memcached::libmemcached支持 Consistent Hashing。

          兩者的接口都與Cache::Memcached幾乎相同,如果正在使用Cache::Memcached, 那么就可以方便地替換過來。Cache::Memcached::Fast重新實現(xiàn)了libketama, 使用Consistent Hashing創(chuàng)建對象時可以指定ketama_points選項。

          my $memcached = Cache::Memcached::Fast->new({
          servers => ["192.168.0.1:11211","192.168.0.2:11211"],
          ketama_points => 150
          });

          另外,Cache::Memcached::libmemcached 是一個使用了Brain Aker開發(fā)的C函數(shù)庫libmemcached的Perl模塊。 libmemcached本身支持幾種分布式算法,也支持Consistent Hashing, 其Perl綁定也支持Consistent Hashing。

          總結(jié)

          本次介紹了memcached的分布式算法,主要有memcached的分布式是由客戶端函數(shù)庫實現(xiàn), 以及高效率地分散數(shù)據(jù)的Consistent Hashing算法。下次將介紹mixi在memcached應(yīng)用方面的一些經(jīng)驗, 和相關(guān)的兼容應(yīng)用程序。

          posted @ 2010-12-15 13:35 楊羅羅 閱讀(1773) | 評論 (1)編輯 收藏

          2010年12月8日

               摘要: java.util.concurrent 包含許多線程安全、測試良好、高性能的并發(fā)構(gòu)建塊。不客氣地說,創(chuàng)建 java.util.concurrent 的目的就是要實現(xiàn) Collection 框架對數(shù)據(jù)結(jié)構(gòu)所執(zhí)行的并發(fā)操作。通過提供一組可靠的、高性能并發(fā)構(gòu)建塊,開發(fā)人員可以提高并發(fā)類的線程安全、可伸縮性、性能、可讀性和可靠性。 如果一些類名看起來相似,可能是因為 java.util.concurr...  閱讀全文

          posted @ 2010-12-08 17:40 楊羅羅 閱讀(816) | 評論 (0)編輯 收藏

          2010年12月3日

          AQS中有一個state字段(int類型,32位)用來描述有多少線程獲持有鎖。在獨占鎖的時代這個值通常是0或者1(如果是重入的就是重入的次數(shù)),在共享鎖的時代就是持有鎖的數(shù)量。
          自旋等待適合于比較短的等待,而掛起線程比較適合那些比較耗時的等待。

          鎖競爭

          影響鎖競爭性的條件有兩個:鎖被請求的頻率和每次持有鎖的時間。顯然當(dāng)而這二者都很小的時候,鎖競爭不會成為主要的瓶頸。但是如果鎖使用不當(dāng),導(dǎo)致二者都比較大,那么很有可能CPU不能有效的處理任務(wù),任務(wù)被大量堆積。

          所以減少鎖競爭的方式有下面三種:

          1. 減少鎖持有的時間
          2. 減少鎖請求的頻率
          3. 采用共享鎖取代獨占鎖

          死鎖

          1.一種情況是線程A永遠(yuǎn)不釋放鎖,結(jié)果B一直拿不到鎖,所以線程B就“死掉”了
          2.第二種情況下,線程A擁有線程B需要的鎖Y,同時線程B擁有線程A需要的鎖X,那么這時候線程A/B互相依賴對方釋放鎖,于是二者都“死掉”了。
          3.如果一個線程總是不能被調(diào)度,那么等待此線程結(jié)果的線程可能就死鎖了。這種情況叫做線程饑餓死鎖。比如說非公平鎖中,如果某些線程非常活躍,在高并發(fā)情況下這類線程可能總是拿到鎖,那么那些活躍度低的線程可能就一直拿不到鎖,這樣就發(fā)生了“饑餓死”。

          避免死鎖的解決方案是:
          1.盡可能的按照鎖的使用規(guī)范請求鎖,另外鎖的請求粒度要小(不要在不需要鎖的地方占用鎖,鎖不用了盡快釋放);
          2.在高級鎖里面總是使用tryLock或者定時機(jī)制(就是指定獲取鎖超時的時間,如果時間到了還沒有獲取到鎖那么就放棄)。高級鎖(Lock)里面的這兩種方式可以有效的避免死鎖。

          posted @ 2010-12-03 10:11 楊羅羅 閱讀(1839) | 評論 (0)編輯 收藏

          2010年11月25日

               摘要: 內(nèi)部類詳解  1、定義    一個類的定義放在另一個類的內(nèi)部,這個類就叫做內(nèi)部類。  Java代碼  public class First {   public class Contents{   &nb...  閱讀全文

          posted @ 2010-11-25 16:27 楊羅羅 閱讀(5032) | 評論 (1)編輯 收藏

          2010年11月24日

          Spring中提供一些Aware相關(guān)接口,像是BeanFactoryAware、 ApplicationContextAware、ResourceLoaderAware、ServletContextAware等等,實現(xiàn)這些 Aware接口的Bean在被初始之后,可以取得一些相對應(yīng)的資源,例如實現(xiàn)BeanFactoryAware的Bean在初始后,Spring容器將會注入BeanFactory的實例,而實現(xiàn)ApplicationContextAware的Bean,在Bean被初始后,將會被注入 ApplicationContext的實例等等。

           Bean取得BeanFactory、ApplicationContextAware的實例目的是什么,一般的目的就是要取得一些檔案資源的存取、相 關(guān)訊息資源或是那些被注入的實例所提供的機(jī)制,例如ApplicationContextAware提供了publishEvent()方法,可以支持基于Observer模式的事件傳播機(jī)制。

           ApplicationContextAware接口的定義如下:

          ApplicationContextAware.java

          public interface ApplicationContextAware {

              void setApplicationContext(ApplicationContext context);

          }


           我們這邊示范如何透過實現(xiàn)ApplicationContextAware注入ApplicationContext來實現(xiàn)事件傳播,首先我們的HelloBean如下:

          HelloBean.java

          package onlyfun.caterpillar;

           

          import org.springframework.context.*;

           

          public class HelloBean implements ApplicationContextAware {

              private ApplicationContext applicationContext;

              private String helloWord = "Hello!World!";

            

              public void setApplicationContext(ApplicationContext context) {

                  this.applicationContext = context;

              }

            

              public void setHelloWord(String helloWord) {

                  this.helloWord = helloWord;

              }

            

              public String getHelloWord() {

                  applicationContext.publishEvent(

                         new PropertyGettedEvent("[" + helloWord + "] is getted"));

                  return helloWord;

              }

          }


           ApplicationContext會由Spring容器注入,publishEvent()方法需要一個繼承ApplicationEvent的對象,我們的PropertyGettedEvent繼承了ApplicationEvent,如下:

          PropertyGettedEvent.java

          package onlyfun.caterpillar;

           

          import org.springframework.context.*;

           

          public class PropertyGettedEvent extends ApplicationEvent {

              public PropertyGettedEvent(Object source) {

                  super(source);

              }

          }


           當(dāng)ApplicationContext執(zhí)行publishEvent()后,會自動尋找實現(xiàn)ApplicationListener接口的對象并通知其發(fā)生對應(yīng)事件,我們實現(xiàn)了PropertyGettedListener如下:

          PrppertyGettedListener.java

          package onlyfun.caterpillar;

           

          import org.springframework.context.*;

           

          public class PropertyGettedListener implements ApplicationListener {

              public void onApplicationEvent(ApplicationEvent event) {

                  System.out.println(event.getSource().toString());  

              }

          }


           Listener必須被實例化,這我們可以在Bean定義檔中加以定義:

          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd">

          <beans>

              <bean id="propertyGetterListener" class="onlyfun.caterpillar.PropertyGettedListener"/>

           

              <bean id="helloBean" class="onlyfun.caterpillar.HelloBean">

                  <property name="helloWord"><value>Hello!Justin!</value></property>

              </bean>

          </beans>


           我們寫一個測試程序來測測事件傳播的運(yùn)行:

          Test.java

          package onlyfun.caterpillar;

           

          import org.springframework.context.*;

          import org.springframework.context.support.*;

           

          public class Test {

              public static void main(String[] args) {

                  ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

                

                  HelloBean hello = (HelloBean) context.getBean("helloBean");

                  System.out.println(hello.getHelloWord());

              }

          }


           執(zhí)行結(jié)果會如下所示:

          log4j:WARN No appenders could be found for logger

          (org.springframework.beans.factory.xml.XmlBeanDefinitionReader).

          log4j:WARN Please initialize the log4j system properly.

          org.springframework.context.support.ClassPathXmlApplicationContext:

          displayName=[org.springframework.context.support.ClassPathXmlApplicationContext;

          hashCode=33219526]; startup date=[Fri Oct 29 10:56:35 CST 2004];

          root of ApplicationContext hierarchy

          [Hello!Justin!] is getted

          Hello!Justin!


           以上是以實現(xiàn)事件傳播來看看實現(xiàn)Aware接口取得對應(yīng)對象后,可以進(jìn)行的動作,同樣的,您也可以實現(xiàn)ResourceLoaderAware接口:

          ResourceLoaderAware.java

          public interface ResourceLoaderAware {

              void setResourceLoader(ResourceLoader loader);

          }


           實現(xiàn)ResourceLoader的Bean就可以取得ResourceLoader的實例,如此就可以使用它的getResource()方法,這對于必須存取檔案資源的Bean相當(dāng)有用。

           基本上,Spring雖然提供了這些Aware相關(guān)接口,然而Bean上若實現(xiàn)了這些界面,就算是與Spring發(fā)生了依賴,從另一個角度來看,雖然您可以直接在Bean上實現(xiàn)這些接口,但您也可以透過setter來完成依賴注入,例如:

          HelloBean.java

          package onlyfun.caterpillar;

           

          import org.springframework.context.*;

           

          public class HelloBean {

              private ApplicationContext applicationContext;

              private String helloWord = "Hello!World!";

            

              public void setApplicationContext(ApplicationContext context) {

                  this.applicationContext = context;

              }

            

              public void setHelloWord(String helloWord) {

                  this.helloWord = helloWord;

              }

            

              public String getHelloWord() {

                  applicationContext.publishEvent(new PropertyGettedEvent("[" + helloWord + "] is getted"));

                  return helloWord;

              }

          }


           注意這次我們并沒有實現(xiàn)ApplicationContextAware,我們在程序中可以自行注入ApplicationContext實例:

          ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

                

          HelloBean hello = (HelloBean) context.getBean("helloBean");

          hello.setApplicationContext(context);

          System.out.println(hello.getHelloWord());


           就Bean而言,降低了對Spring的依賴,可以比較容易從現(xiàn)有的框架中脫離。

           

          posted @ 2010-11-24 11:14 楊羅羅 閱讀(7659) | 評論 (1)編輯 收藏

          2010年11月19日

          Hibernate的二級緩存策略的一般過程如下:

            1) 條件查詢的時候,總是發(fā)出一條select * from table_name where …. (選擇所有字段)這樣的SQL語句查詢數(shù)據(jù)庫,一次獲得所有的數(shù)據(jù)對象。

            2) 把獲得的所有數(shù)據(jù)對象根據(jù)ID放入到第二級緩存中。

            3) 當(dāng)Hibernate根據(jù)ID訪問數(shù)據(jù)對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那么從二級緩存中查;查不到,再查詢數(shù)據(jù)庫,把結(jié)果按照ID放入到緩存。

            4) 刪除、更新、增加數(shù)據(jù)的時候,同時更新緩存。

            Hibernate的二級緩存策略,是針對于ID查詢的緩存策略,對于條件查詢則毫無作用。為此,Hibernate提供了針對條件查詢的Query緩存。

            Hibernate的Query緩存策略的過程如下:

            1) Hibernate首先根據(jù)這些信息組成一個Query Key,Query Key包括條件查詢的請求一般信息:SQL, SQL需要的參數(shù),記錄范圍(起始位置rowStart,最大記錄個數(shù)maxRows),等。

            2) Hibernate根據(jù)這個Query Key到Query緩存中查找對應(yīng)的結(jié)果列表。如果存在,那么返回這個結(jié)果列表;如果不存在,查詢數(shù)據(jù)庫,獲取結(jié)果列表,把整個結(jié)果列表根據(jù)Query Key放入到Query緩存中。

            3) Query Key中的SQL涉及到一些表名,如果這些表的任何數(shù)據(jù)發(fā)生修改、刪除、增加等操作,這些相關(guān)的Query Key都要從緩存中清空。

          posted @ 2010-11-19 11:33 楊羅羅 閱讀(773) | 評論 (0)編輯 收藏

          2010年11月18日

          在JDK 5之前Java語言是靠synchronized關(guān)鍵字保證同步的,這會導(dǎo)致有鎖(后面的章節(jié)還會談到鎖)。

          鎖機(jī)制存在以下問題:

          (1)在多線程競爭下,加鎖、釋放鎖會導(dǎo)致比較多的上下文切換和調(diào)度延時,引起性能問題。

          (2)一個線程持有鎖會導(dǎo)致其它所有需要此鎖的線程掛起。

          (3)如果一個優(yōu)先級高的線程等待一個優(yōu)先級低的線程釋放鎖會導(dǎo)致優(yōu)先級倒置,引起性能風(fēng)險。

          volatile是不錯的機(jī)制,但是volatile不能保證原子性。因此對于同步最終還是要回到鎖機(jī)制上來。

          獨占鎖是一種悲觀鎖,synchronized就是一種獨占鎖,會導(dǎo)致其它所有需要鎖的線程掛起,等待持有鎖的線程釋放鎖。而另一個更加有效的鎖就是樂觀鎖。所謂觀鎖就是,每次不加鎖而是假設(shè)沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。

          CAS 操作

          上面的樂觀鎖用到的機(jī)制就是CAS,Compare and Swap。

          CAS有3個操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值V修改為B,否則什么都不做。

          非阻塞算法 (nonblocking algorithms)

          一個線程的失敗或者掛起不應(yīng)該影響其他線程的失敗或掛起的算法。

          現(xiàn)代的CPU提供了特殊的指令,可以自動更新共享數(shù)據(jù),而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定。

          拿出AtomicInteger來研究在沒有鎖的情況下是如何做到數(shù)據(jù)正確性的。

          private volatile int value;

          首先毫無以為,在沒有鎖的機(jī)制下可能需要借助volatile原語,保證線程間的數(shù)據(jù)是可見的(共享的)。這樣才獲取變量的值的時候才能直接讀取。

          public final int get() {
                  return value;
              }

          然后來看看++i是怎么做到的。

          public final int incrementAndGet() {
              for (;;) {
                  int current = get();
                  int next = current + 1;
                  if (compareAndSet(current, next))
                      return next;
              }
          }

          在這里采用了CAS操作,每次從內(nèi)存中讀取數(shù)據(jù)然后將此數(shù)據(jù)和+1后的結(jié)果進(jìn)行CAS操作,如果成功就返回結(jié)果,否則重試直到成功為止。

          而compareAndSet利用JNI來完成CPU指令的操作。

          public final boolean compareAndSet(int expect, int update) {   
              return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
              }

          整體的過程就是這樣子的,利用CPU的CAS指令,同時借助JNI來完成Java的非阻塞算法。其它原子操作都是利用類似的特性完成的。

          而整個J.U.C都是建立在CAS之上的,因此對于synchronized阻塞算法,J.U.C在性能上有了很大的提升。

          CAS看起來很爽,但是會導(dǎo)致“ABA問題”。

          CAS算法實現(xiàn)一個重要前提需要取出內(nèi)存中某時刻的數(shù)據(jù),而在下時刻比較并替換,那么在這個時間差類會導(dǎo)致數(shù)據(jù)的變化

          比如說一個線程one從內(nèi)存位置V中取出A,這時候另一個線程two也從內(nèi)存中取出A,并且two進(jìn)行了一些操作變成了B,然后two又將V位置的數(shù)據(jù)變成A,這時候線程one進(jìn)行CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A,然后one操作成功。盡管線程one的CAS操作成功,但是不代表這個過程就是沒有問題的。如果鏈表的頭在變化了兩次后恢復(fù)了原值,但是不代表鏈表就沒有變化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。這允許一對變化的元素進(jìn)行原子操作。

          posted @ 2010-11-18 15:16 楊羅羅 閱讀(3124) | 評論 (1)編輯 收藏

          主站蜘蛛池模板: 宝山区| 射阳县| 双辽市| 剑川县| 荆州市| 阿巴嘎旗| 平安县| 原阳县| 西安市| 盐山县| 舟山市| 社旗县| 香格里拉县| 娄底市| 西安市| 黔南| 平顶山市| 隆林| 大埔区| 灵武市| 新龙县| 胶州市| 陈巴尔虎旗| 星子县| 河津市| 金川县| 枞阳县| 稷山县| 靖远县| 广安市| 神池县| 舟曲县| 太湖县| 大关县| 凌源市| 大足县| 大港区| 嘉荫县| 武夷山市| 资中县| 蒙自县|