莊周夢蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理

              xmemcached發(fā)布1.10 RC2,這一版本的主要改進如下:
          1、修復(fù)1.10-RC1以來發(fā)現(xiàn)的bug

          2、添加對flush_all協(xié)議的支持,XMemcachedClient.flushAll方法用以使memcached的緩存數(shù)據(jù)項失效,這一方法有系列重載方法

           void     flushAll()
                    使cache中所有的數(shù)據(jù)項失效,如果是連接多個節(jié)點的memcached,那么所有的memcached中的數(shù)據(jù)項都將失效
           void     flushAll(long timeout)
                    使cache中所有的數(shù)據(jù)項失效,如果是連接多個節(jié)點的memcached,那么所有的memcached中的數(shù)據(jù)項都將失效


           
          void     flushAll(java.net.InetSocketAddress address)
                   
          使指定memcached節(jié)點的數(shù)據(jù)項失效
           void     flushAll(java.net.InetSocketAddress address, long timeout)
                   
          使指定memcached節(jié)點的數(shù)據(jù)項失效

          void     flushAll(java.lang.String host)
                    使指定memcached節(jié)點的數(shù)據(jù)項失效
          void     flushAll(java.lang.String host, long timeout)
                    使指定memcached節(jié)點的數(shù)據(jù)項失效

          使用方法,例子:

          XMemcachedClientBuilder builder = new XMemcachedClientBuilder(
                              AddrUtil.getAddresses(
          "192.168.0.121:12000 192.168.0.121:12001"));
          XMemcachedClient client 
          = builder.build();
          // 使所有數(shù)據(jù)項失效
          client.flushAll(); 
          //使192.168.0.121:12000節(jié)點的數(shù)據(jù)項失效
          client.flushAll("192.168.0.121:12000");


          3、添加stats協(xié)議支持,用以查詢某個memcached server的統(tǒng)計信息:
          java.util.Map<java.lang.String,java.lang.String>     stats(java.net.InetSocketAddress address)
                   
          查看特定節(jié)點的memcached server統(tǒng)計信息
           java.util.Map<java.lang.String,java.lang.String>     stats(java.net.InetSocketAddress address, long timeout)
                    查看特定節(jié)點的memcached server統(tǒng)計信息
           java.util.Map
          <java.lang.String,java.lang.String>     stats(java.lang.String host)
                    
          查看特定節(jié)點的memcached server統(tǒng)計信息
           java.util.Map<java.lang.String,java.lang.String>     stats(java.lang.String host, long timeout)
                    查看特定節(jié)點的memcached server統(tǒng)計信息

              查詢結(jié)果以map的形式返回,map中的key-value映射與stats協(xié)議返回的一致。例子:

          //查詢192.168.0.121:12000節(jié)點的統(tǒng)計信息
          Map<String,String> info=xmemcachedClient.stats("192.168.0.121:12000")

          4、允許通過jmx監(jiān)控XMemcachedClient的運行狀況,并調(diào)整參數(shù)等。jmx監(jiān)控默認未開啟,可以通過

          -Dxmemcached.jmx.enable=true -Dxmemcached.rmi.port=7077 -Dxmemcached.rmi.name=xmemcachedServer

          的參數(shù)來啟用,那么就可以通過
          service:jmx:rmi:///jndi/rmi://[host]:7077/xmemcachedServer

          來訪問MBean。

          XMemcached提供兩個標準MBean,一個是net.rubyeye.xmemcached.impl.OptimiezerMBean,用于查看和調(diào)整性能參數(shù);另一個是net.rubyeye.xmemcached.monitor.StatisticsHandlerMBean,用于查詢memcached客戶端的統(tǒng)計信息(注意跟memcached server的統(tǒng)計信息做區(qū)別,客戶端的統(tǒng)計信息可能包括了多個節(jié)點)。默認統(tǒng)計未開啟,可以通過

          -Dxmemcached.statistics.enable=true

          來開啟客戶端統(tǒng)計。更多信息請用jconsole訪問即知。

            下載地址在這里




          posted @ 2009-05-05 20:03 dennis 閱讀(1546) | 評論 (0)編輯 收藏

              以下內(nèi)容純屬虛構(gòu),如有雷同純屬巧合。
              今天在偉大、光榮的xlands group里閑聊的時候,阿寶同學(xué)提到在上海被隔離的墨西哥航班乘客們被安排在一家酒店,并且房間里提供上網(wǎng)服務(wù)。俺不大靈光的腦袋突然靈光一閃,冒出個疑問:生物病毒可以通過比特傳播嗎?如果生物病毒可以通過比特傳播,那么互聯(lián)網(wǎng)將成為世界上最大的恐怖網(wǎng)絡(luò)。
              讓我們來認真研究這個問題,網(wǎng)絡(luò)上傳播只能是0和1的組成的字節(jié),流感的DNA可以編碼成一個字節(jié)流,通過網(wǎng)絡(luò)傳輸?shù)矫刻鞕C器上,然后組裝成致命病毒,并通過空氣傳播感染人群。這里有幾個問題:
          1、DNA編碼成字節(jié)流,數(shù)據(jù)量會不會太大?這個可以通過挑選特定的病菌來解決,或者下面要談到的需要硬件廠商配合的方法。、

          2、怎么在用戶的機器上合成致命病菌?要知道字節(jié)序列是無生命的,一個解決辦法就是需要硬件廠商來配合,在芯片或者網(wǎng)卡中預(yù)先通過某種高科技的控制器存儲足量的合成病菌所需要的DNA、蛋白質(zhì)等等。當(dāng)網(wǎng)卡接收到特殊特征的字節(jié)序列(比如“你好他也好”)就啟動這個高科技控制器,吭哧吭哧地合成病毒,然后通過電腦風(fēng)扇傳播到空氣中。更進一步,硬件中可以預(yù)先存放一些致命病毒(咳咳,我覺的埃博拉病毒不錯),然后在地球某個陰暗的角落(很可能是北極,變形金剛和超人都在那),一只黑手按下一個神奇的紅色按鈕,向全世界安裝了此類硬件的聯(lián)網(wǎng)機器發(fā)出指令釋放病毒,你說世界會怎么樣?
             
              這種可怕、恐怖、無敵的網(wǎng)絡(luò)恐怖主義,似乎沒有什么抵抗方法,俺這里再探討下可行的預(yù)防方法:
          1、永遠不上網(wǎng)
          2、機器使用一年后就淘汰,防止機器老朽病毒泄漏
          3、使用龍芯牌CPU
          4、山寨整機
          5、移居火星

             本論文版權(quán)所有,如有抄襲,板磚伺候。



          posted @ 2009-05-04 15:56 dennis 閱讀(438) | 評論 (2)編輯 收藏

          每個第一次構(gòu)建分布式系統(tǒng)的人都可能會做出8個錯誤的對網(wǎng)絡(luò)的假設(shè):
          1. The network is reliable
          2. Latency is zero
          3. Bandwidth is infinite
          4. The network is secure
          5. Topology doesn't change
          6. There is one administrator
          7. Transport cost is zero
          8. The network is homogeneous

          翻譯過來就是:
          1、網(wǎng)絡(luò)是穩(wěn)定可靠的
          2、沒有延遲
          3、帶寬無限
          4、網(wǎng)絡(luò)是安全的
          5、網(wǎng)絡(luò)拓撲不會改變
          6、只有一個管理員
          7、傳輸成本為0
          8、網(wǎng)絡(luò)是均勻的,現(xiàn)實是各種網(wǎng)絡(luò)環(huán)境都有。

          更多錯誤假設(shè):
          1、網(wǎng)絡(luò)IO跟磁盤IO一樣
              網(wǎng)絡(luò)IO比之磁盤IO更不可預(yù)測、不可靠和不可控,網(wǎng)絡(luò)IO包括了軟硬件兩方面的限制。
          2、你與對端能夠同步
            你無法假設(shè)對端是否關(guān)閉、接收到數(shù)據(jù),這些通常需要你在應(yīng)用協(xié)議里同步。
          3、所有的錯誤都可以被檢測到。
              很多錯誤例如對端關(guān)閉引起的讀阻塞都需要應(yīng)用層來處理。

          4、資源無限可用。
          5、應(yīng)用可以無限等待一個遠程服務(wù)
              任何大規(guī)模的應(yīng)用都需要慎重設(shè)計超時、過期策略

          6、遠程服務(wù)總能響應(yīng)及時。
          7、只有單點失敗
          8、只有一個資源分配器
          9、只有一個時間,也就是全局時間的問題。

          posted @ 2009-05-02 13:02 dennis 閱讀(1797) | 評論 (0)編輯 收藏

              最近讀薛涌的《學(xué)而時習(xí)之》,知道這本書是因為在《南方周末》上看到。閑話不說,其實這本書的精華都在那個序里,咱水平很有限,談?wù)勎业目捶āP蜓詥蔚吨比氲乇硎局袊幕鞘。τ颗袛辔幕欠袷〉囊粋€標準很簡單:

              幾年前我去醫(yī)院,當(dāng)我的白人醫(yī)生知道我是來自中國、而且研究中國歷史后,馬上告訴我他在大學(xué)讀過明史的課,非常景仰中國文化。我當(dāng)時聽不出他到底是 出于客氣還是出于真誠,干脆直率地告訴他我的看法:從現(xiàn)代歷史的角度說,中國文化是個失敗的文化,至少不能說是個成功的文化。對方聽了很吃驚,馬上拿出文 化相對主義那一套和我辯論。我知道醫(yī)生惜時如金、無法在看病時開一個中國文化的討論班,就單刀直入地問他:“我愿意我和我的孩子生活在這里。你希望你或你 的后代生活在那里嗎?”他一時語塞。

              我判定中國文化成功與失敗的標準就這么簡單。我是個中國人,我的大夫是個白人。但是,我們是完 全平等的。我們中國人應(yīng)該享受這些白人所享受的生活,比如有相當(dāng)高的經(jīng)濟收入,在憲政之下?lián)碛凶约旱恼螜?quán)利,等等。同生而為人,憑什么人家有這些而我們 沒有?憑什么在人家有這些而我們沒有時,還不能說我們失敗了?

              我第一次看到這個論調(diào)的時候很是震撼,乍一看似乎說的非常在理。這個標準確實簡單,一個美國人,愿意跟我調(diào)換生活嗎?先別噴,如果我的家人朋友在美國,那我肯定愿意去美國生活,因為至少那邊沒有瘦肉精,沒有城管,不用打醬油。那么這個美國人愿意嗎?那就很值的懷疑了。無論答案是肯不肯,這個遲疑本身就讓薛涌勝利了。
              這個判斷文化失敗與否的標準簡單粗暴,卻似乎直接有效。然而我卻產(chǎn)生個疑問,這個標準對比的當(dāng)下,而當(dāng)下的中國還有多少傳統(tǒng)文化的影子很值的懷疑;如果要對比當(dāng)下,可能拿臺灣人跟美國人對比更有意義,臺灣保存的中華傳統(tǒng)肯定比大陸多的多,那么一個臺灣人愿意跟美國人調(diào)換生活嗎?你無法簡單地下結(jié)論了。再遠些,如果拿這個標準去對比古代,一個唐朝人愿意跟那個時期的歐洲人(咱就不跟美洲印第安人比了)調(diào)換生活嗎?這時候的你可能更偏向咱中國文化了,畢竟是盛唐時期嘛。如果從更長的時間維度看,單純按照這么個簡單粗暴的標準來衡量,中國文明似乎還相當(dāng)長時間內(nèi)領(lǐng)先于西方文明嘛。因此,還是《莫以成敗論文化/文明

          posted @ 2009-05-01 23:40 dennis 閱讀(493) | 評論 (0)編輯 收藏

          update:緊急修復(fù)一個嚴重的bug,影響多節(jié)點memcached下的余數(shù)哈希分布。jmx監(jiān)控正式啟用。更多單元測試。

              XMemcached是一個基于java nio的memcached客戶端。最新發(fā)布1.10-RC1版本,這個版本其實早就完成,一直沒有環(huán)境來測試,本機測試沒有多大價值。今天做了一個初步測試,在效率上已經(jīng)超越了spymemcached最新的2.3.1版本,具體的測試數(shù)據(jù)請看下面,下載地址這里,更新了wiki

             1.10-RC1的主要改進:
          1、性能優(yōu)化,具體請參見wiki,在測試中已經(jīng)超越了spymemcached最新的2.3.1版本
          2、重構(gòu),引入Optimiezer、XMemcachedClientBuilder等類,并引入泛型方法,使API接口更趨實用性和便捷性。1.10rc1將不兼容1.0版本,具體API請參見javadoc。可以保證的是在1.10穩(wěn)定后,xmemcached在API接口不會再有大的變動。
          3、引入maven做項目構(gòu)建,原來的ant構(gòu)建仍然可用。
          4、bug修復(fù),具體參見issues報告,更多測試提高健壯性。
          5、引入jmx監(jiān)控,可以通過java -Dxmemcached.jmx.enable=true來啟用jmx監(jiān)控,jmx功能可以統(tǒng)計xmemcached的各種操作次數(shù),以及優(yōu)化參數(shù)調(diào)整等,更多信息請參考javadoc和wiki

             后續(xù)計劃:
          1.10版本的正式發(fā)布
          1.11版本的開發(fā)工作,引入JMX監(jiān)控。

              今天給出的是memcached存儲java原生類型的效率測試,key和value都是字符串,memcached單節(jié)點跑在局域網(wǎng)內(nèi)的服務(wù)器上,spymemcached使用的是2.3.1版本,xmemcached使用的是1.10-RC1,兩者都是默認配置。具體圖表請看下面及相應(yīng)說明。
              首先是在我本機windows xp環(huán)境(因為主要看對比,機器環(huán)境不再具體說明),連接memcached服務(wù)器所做的測試,下面是xmemcached和spymemcached的set操作效率對比,縱坐標為TPS,橫坐標為線程并發(fā)數(shù)(下同)。



              顯然,兩者的set操作效率在windows下極為接近。接下來是get操作的對比直方圖:


             
              XMemcached在get操作上保持傳統(tǒng)優(yōu)勢,并發(fā)越大,優(yōu)勢越明顯。在測試了windows下兩個客戶端的表現(xiàn),繼續(xù)做linux下的測試數(shù)據(jù),因為所用的機器是一臺8核的牛機,因此TPS非常驚人。首先是set操作的對比:
              


              不用多說,xmemcached全面占優(yōu)。再看get操作的對比:



              一個奇特的現(xiàn)象是在200線程并發(fā)的時候,spymemcached反而超過了xmemcached,這一現(xiàn)象反復(fù)測試還是如此。其他并發(fā)下,xmemcached全面占優(yōu),在達到500并發(fā)時,spymemcached的get很多超時,因此數(shù)據(jù)作廢,而xmemcached由于可以設(shè)置get的操作的超時時間更長,因此仍然正常運行。
              從以上測試可以看出,1.10-RC1版本的XMemcached存儲原生類型已經(jīng)在windows和linux平臺上的效率都超過了spymemcached。對于自定義對象和容器對象的存儲測試也證明xmemcached的效率已經(jīng)全面超越了spymemcached。很希望有更多的測試報告,畢竟我的測試可能還是不夠客觀。



          posted @ 2009-04-28 21:50 dennis 閱讀(1737) | 評論 (2)編輯 收藏


          1、在構(gòu)造函數(shù)中啟動線程
               我在很多代碼中都看到這樣的問題,在構(gòu)造函數(shù)中啟動一個線程,類似這樣:
          public class A{
             
          public A(){
                
          this.x=1;
                
          this.y=2;
                
          this.thread=new MyThread();
                
          this.thread.start();
             }
             
          }
             這個會引起什么問題呢?如果有個類B繼承了類A,依據(jù)java類初始化的順序,A的構(gòu)造函數(shù)一定會在B的構(gòu)造函數(shù)調(diào)用前被調(diào)用,那么thread線程也將在B被完全初始化之前啟動,當(dāng)thread運行時使用到了類A中的某些變量,那么就可能使用的不是你預(yù)期中的值,因為在B的構(gòu)造函數(shù)中你可能賦給這些變量新的值。也就是說此時將有兩個線程在使用這些變量,而這些變量卻沒有同步。
             解決這個問題有兩個辦法:將A設(shè)置為final,不可繼承;或者提供單獨的start方法用來啟動線程,而不是放在構(gòu)造函數(shù)中。

          2、不完全的同步
            
          都知道對一個變量同步的有效方式是用synchronized保護起來,synchronized可能是對象鎖,也可能是類鎖,看你是類方法還是實例方法。但是,當(dāng)你將某個變量在A方法中同步,那么在變量出現(xiàn)的其他地方,你也需要同步,除非你允許弱可見性甚至產(chǎn)生錯誤值。類似這樣的代碼:
          class A{
            
          int x;
            
          public int getX(){
               
          return x;
            }
            
          public synchronized void setX(int x)
            {
               
          this.x=x;
            }
          }
              x的setter方法有同步,然而getter方法卻沒有,那么就無法保證其他線程通過getX得到的x是最新的值。事實上,這里的setX的同步是沒有必要的,因為對int的寫入是原子的,這一點JVM規(guī)范已經(jīng)保證,多個同步?jīng)]有任何意義;當(dāng)然,如果這里不是int,而是double或者long,那么getX和setX都將需要同步,因為double和long都是64位,寫入和讀取都是分成兩個32位來進行(這一點取決于jvm的實現(xiàn),有的jvm實現(xiàn)可能保證對long和double的read、write是原子的),沒有保證原子性。類似上面這樣的代碼,其實都可以通過聲明變量為volatile來解決。
            
          3、在使用某個對象當(dāng)鎖時,改變了對象的引用,導(dǎo)致同步失效。
             這也是很常見的錯誤,類似下面的代碼:
          synchronized(array[0])
          {
             ......
             array[0]=new A();
             ......
          }
             同步塊使用array[0]作為鎖,然而在同步塊中卻改變了array[0]指向的引用。分析下這個場景,第一個線程獲取了array[0]的鎖,第二個線程因為無法獲取array[0]而等待,在改變了array[0]的引用后,第三個線程獲取了新的array[0]的鎖,第一和第三兩個線程持有的鎖是不一樣的,同步互斥的目的就完全沒有達到了。這樣代碼的修改,通常是將鎖聲明為final變量或者引入業(yè)務(wù)無關(guān)的鎖對象,保證在同步塊內(nèi)不會被修改引用。

          4、沒有在循環(huán)中調(diào)用wait()。

              wait和notify用于實現(xiàn)條件變量,你可能知道需要在同步塊中調(diào)用wait和notify,為了保證條件的改變能做到原子性和可見性。常常看見很多代碼做到了同步,卻沒有在循環(huán)中調(diào)用wait,而是使用if甚至沒有條件判斷:
          synchronized(lock)
          {
            
          if(isEmpty()
               lock.wait();
             
          }
              對條件的判斷是使用if,這會造成什么問題呢?在判斷條件之前可能調(diào)用notify或者notifyAll,那么條件已經(jīng)滿足,不會等待,這沒什么問題。在條件沒有滿足,調(diào)用了wait()方法,釋放lock鎖并進入等待休眠狀態(tài)。如果線程是在正常情況下,也就是條件被改變之后被喚醒,那么沒有任何問題,條件滿足繼續(xù)執(zhí)行下面的邏輯操作。問題在于線程可能被意外甚至惡意喚醒,由于沒有再次進行條件判斷,在條件沒有被滿足的情況下,線程執(zhí)行了后續(xù)的操作。意外喚醒的情況,可能是調(diào)用了notifyAll,可能是有人惡意喚醒,也可能是很少情況下的自動蘇醒(稱為“偽喚醒”)。因此為了防止這種條件沒有滿足就執(zhí)行后續(xù)操作的情況,需要在被喚醒后再次判斷條件,如果條件不滿足,繼續(xù)進入等待狀態(tài),條件滿足,才進行后續(xù)操作。
          synchronized(lock)
          {
             
          while(isEmpty()
               lock.wait();
             
          }
              沒有進行條件判斷就調(diào)用wait的情況更嚴重,因為在等待之前可能notify已經(jīng)被調(diào)用,那么在調(diào)用了wait之后進入等待休眠狀態(tài)后就無法保證線程蘇醒過來。

          5、同步的范圍過小或者過大。

              同步的范圍過小,可能完全沒有達到同步的目的;同步的范圍過大,可能會影響性能。同步范圍過小的一個常見例子是誤認為兩個同步的方法一起調(diào)用也是將同步的,需要記住的是Atomic+Atomic!=Atomic。
             Map map=Collections.synchronizedMap(new HashMap());
            
          if(!map.containsKey("a")){
                      map.put(
          "a", value);
             }
             這是一個很典型的錯誤,map是線程安全的,containskey和put方法也是線程安全的,然而兩個線程安全的方法被組合調(diào)用就不一定是線程安全的了。因為在containsKey和put之間,可能有其他線程搶先put進了a,那么就可能覆蓋了其他線程設(shè)置的值,導(dǎo)致值的丟失。解決這一問題的方法就是擴大同步范圍,因為對象鎖是可重入的,因此在線程安全方法之上再同步相同的鎖對象不會有問題。
                 Map map = Collections.synchronizedMap(new HashMap());
                
          synchronized (map) {
                      
          if (!map.containsKey("a")) {
                          map.put(
          "a", value);
                      }
                  }
             注意,加大鎖的范圍,也要保證使用的是同一個鎖,不然很可能造成死鎖。 Collections.synchronizedMap(new HashMap())使用的鎖是map本身,因此沒有問題。當(dāng)然,上面的情況現(xiàn)在更推薦使用ConcurrentHashMap,它有putIfAbsent方法來達到同樣的目的并且滿足線程安全性。

              同步范圍過大的例子也很多,比如在同步塊中new大對象,或者調(diào)用費時的IO操作(操作數(shù)據(jù)庫,webservice等)。不得不調(diào)用費時操作的時候,一定要指定超時時間,例如通過URLConnection去invoke某個URL時就要設(shè)置connect timeout和read timeout,防止鎖被獨占不釋放。
          同步范圍過大的情況下,要在保證線程安全的前提下,將不必要同步的操作從同步塊中移出。

          6、正確使用volatile
              在jdk5修正了volatile的語義后,volatile作為一種輕量級的同步策略就得到了大量的使用。volatile的嚴格定義參考jvm spec,這里只從volatile能做什么,和不能用來做什么出發(fā)做個探討。

          volatile可以用來做什么?

          1)狀態(tài)標志,模擬控制機制。常見用途如控制線程是否停止:
          private volatile boolean stopped;
          public void close(){
             stopped
          =true;
          }

          public void run(){

             
          while(!stopped){
                
          //do something
             }
             
          }
            

              前提是do something中不會有阻塞調(diào)用之類。volatile保證stopped變量的可見性,run方法中讀取stopped變量總是main memory中的最新值。

          2)安全發(fā)布,如修復(fù)DLC問題,具體參考http://www.javaeye.com/topic/260515和http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html
              private volatile IoBufferAllocator instance;
              
          public IoBufferAllocator getInsntace(){
                  
          if(instance==null){
                      
          synchronized (IoBufferAllocator.class) {
                          
          if(instance==null)
                              instance
          =new IoBufferAllocator();
                      }
                  }
                  
          return instance;
              }

          3)開銷較低的讀寫鎖
          public class CheesyCounter {
              private volatile int value;

              
          public int getValue() { return value; }

              
          public synchronized int increment() {
                  
          return value++;
              }
          }

          synchronized保證更新的原子性,volatile保證線程間的可見性。

          volatile不能用于做什么?
          1)不能用于做計數(shù)器
              public class CheesyCounter {
                  
          private volatile int value;

                  
          public int getValue() { return value; }

                  
          public int increment() {
                      
          return value++;
                  }
              }

              因為value++其實是有三個操作組成的:讀取、修改、寫入,volatile不能保證這個序列是原子的。對value的修改操作依賴于value的最新值。解決這個問題的方法可以將increment方法同步,或者使用AtomicInteger原子類。

          2)與其他變量構(gòu)成不變式
             一個典型的例子是定義一個數(shù)據(jù)范圍,需要保證約束lower<upper。
          public class NumberRange {
              
          private volatile int lower, upper;

              
          public int getLower() { return lower; }
              
          public int getUpper() { return upper; }

              
          public void setLower(int value) { 
                  
          if (value > upper) 
                      
          throw new IllegalArgumentException();
                  lower 
          = value;
              }

              
          public void setUpper(int value) { 
                  
          if (value < lower) 
                      
          throw new IllegalArgumentException();
                  upper 
          = value;
              }
          }

              盡管講lower和upper聲明為volatile,但是setLower和setUpper并不是線程安全方法。假設(shè)初始狀態(tài)為(0,5),同時調(diào)用setLower(4)和setUpper(3),兩個線程交叉進行,最后結(jié)果可能是(4,3),違反了約束條件。修改這個問題的辦法就是將setLower和setUpper同步:
          public class NumberRange {
              
          private volatile int lower, upper;

              
          public int getLower() { return lower; }
              
          public int getUpper() { return upper; }

              
          public synchronized void setLower(int value) { 
                  
          if (value > upper) 
                      
          throw new IllegalArgumentException();
                  lower 
          = value;
              }

              
          public synchronized void setUpper(int value) { 
                  
          if (value < lower) 
                      
          throw new IllegalArgumentException();
                  upper 
          = value;
              }
          }

           

          posted @ 2009-04-25 12:21 dennis 閱讀(3204) | 評論 (2)編輯 收藏

          1、散列表要解決的一個問題就是散列值的沖突問題,通常是兩種方法:鏈表法和開放地址法。鏈表法就是將相同hash值的對象組織成一個鏈表放在hash值對應(yīng)的槽位;開放地址法是通過一個探測算法,當(dāng)某個槽位已經(jīng)被占據(jù)的情況下繼續(xù)查找下一個可以使用的槽位。java.util.HashMap采用的鏈表法的方式,鏈表是單向鏈表,因此在刪除過程中要自己維持prev節(jié)點,我想不采用雙向鏈表是從節(jié)省空間考慮。一個典型的查找過程:
          for (Entry<K,V> e = table[indexFor(hash, table.length)];
                       e 
          != null;
                       e 
          = e.next) {
                      Object k;
                      
          if (e.hash == hash &&
                          ((k 
          = e.key) == key || (key != null && key.equals(k))))
                          
          return e;
           }
             HashMap采用鏈表法而不是開放地址法,猜想可能的原因是從實用角度出發(fā),對空間和時間效率做出的折中選擇。采用開放地址法,無論是線性探測或者二次探測都可能造成群集現(xiàn)象,而雙重散列會要求散列表的裝填程度比較低的情況下會有比較好的查找效率,容易造成空間的浪費。

          2、什么是負載因子?負載因子a定義為
               a=散列表的實際元素數(shù)目(n)/ 散列表的容量(m)

          負載因子衡量的是一個散列表的空間的使用程度,負載因子越大表示散列表的裝填程度越高,反之愈小。對于使用鏈表法的散列表來說,查找一個元素的平均時間是 O(1+a),因此如果負載因子越大,對空間的利用更充分,然而后果是查找效率的降低;如果負載因子太小,那么散列表的數(shù)據(jù)將過于稀疏,對空間造成嚴重浪費。

          回到HashMap的實現(xiàn),HashMap中的loadFactor其實定義的就是該map對象允許的最大的負載因子,如果超過這個系數(shù)將重新resize。這個是通過threshold字段來判斷,看threshold的計算:
          threshold = (int)(capacity * loadFactor);

          結(jié)合上面的負載因子的定義公式可知,threshold就是在此loadFactor和capacity對應(yīng)下允許的最大元素數(shù)目,超過這個數(shù)目就重新resize,以降低實際的負載因子。默認的的負載因子0.75是對空間和時間效率的一個平衡選擇。注意到的一點是resize的規(guī)模是現(xiàn)有 capacity的兩倍:
            if (size++ >= threshold)
                      resize(
          2 * table.length);
           
          3、可能你也注意到了,java.util.HashMap對key的hash值多做了一步處理,而不是直接使用hashCode:

          static int hash(int h) {
                  h 
          ^= (h >>> 20^ (h >>> 12);
                  
          return h ^ (h >>> 7^ (h >>> 4);
            }

          這個處理的原因在于HashMap的容量總是采用2的p次冪,而取index(槽位)的方法是
          static int indexFor(int h, int length) {
                  
          return h & (length-1);
           }

          這一運算等價于對length取模,也就是
                 h % 2^p
          返回的將是h的p個最低位組成的數(shù)字,我們假設(shè)hash輸入是符合簡單一致散列,然而這一假設(shè)并不能推論出hash的p個最低位也會符合簡單一致散列,也許h的這p個最低位相同的幾率很大,那么沖突的幾率就非常大了。優(yōu)秀的散列函數(shù)應(yīng)該需要考慮所有的位。

          因此為了防止這些“壞”的散列函數(shù)造成效率的降低,HashMap預(yù)先對hash值做了處理以考慮到所有的位,根據(jù)注釋也可以知道。這個處理我看不懂,留待高人解釋,也許來自于某本算法書也不一定。

          4、我們知道java.util.HashMap不是線程安全的,因此如果在使用迭代器的過程中有其他線程修改了map,那么將拋出ConcurrentModificationException,這就是所謂fail-fast策略(速錯),這一策略在源碼中的實現(xiàn)是通過 modCount域,modCount顧名思義就是修改次數(shù),對HashMap內(nèi)容的修改都將增加這個值,那么在迭代器初始化過程中會將這個值賦給迭代器的expectedModCount,

           HashIterator() {
                      expectedModCount 
          = modCount;
                      
          if (size > 0) { // advance to first entry
                          Entry[] t = table;
                          
          while (index < t.length && (next = t[index++]) == null)
                              ;
                      }
                  }

          在迭代過程中,判斷modCount跟expectedModCount是否相等,如果不相等就表示已經(jīng)有其他線程修改了map

            
          final Entry<K,V> nextEntry() {
                      
          if (modCount != expectedModCount)
                          
          throw new ConcurrentModificationException();
           注意到modCount聲明為volatile,保證線程之間修改的可見性。
           




          posted @ 2009-04-15 12:33 dennis 閱讀(4388) | 評論 (0)編輯 收藏

              小毅同學(xué)最近有個煩心事,就是他的腦袋有點偏,由于長期腦袋歪向右邊睡,導(dǎo)致他的大腦袋右扁左高,從上往下看,極
          其不自然。這個問題讓他爺爺奶奶、爸爸媽媽很煩心,最近計劃著給他買個定型枕,三個月了,可以睡枕頭了,趁還矯正得過
          來的時候趕緊矯正,他爸爸也叮囑媽媽要讓他側(cè)睡,一個晚上仰躺,一個晚上左側(cè)睡,希望能慢慢好起來。不然以后找不到媳
          婦可能要怨死他老媽。
              小毅同學(xué)也有個非常不好的習(xí)慣,要睡覺的時候不是乖乖睡覺,而是拼命哭鬧,非要別人抱著睡。抱著睡也還罷了,還要求
          抱的人不能坐不能停,一停或坐就又開始挺直了身體不舒坦起來了,必須在屋內(nèi)走來走去,看他那小臉,很喜歡別人抱著一晃一
          晃的感覺。小毅同學(xué)睡覺也是很有趣,一開始是假寐,時刻注意抱的人是否停留,偶爾張開眼瞄你一下馬上閉上,我說他這是在
          觀察敵情呢。過那么一會,眼睛不睜了,那時候你就可以停下來坐著抱了,但是這時候還不能馬上放床上,你一放床上他也會發(fā)
          覺醒過來,你不得不重復(fù)“散步”這一階段,因此最好是坐著抱他一會,等他睡熟了再放床上去。
            小毅同學(xué)剛出生到2個月的時候,基本不笑,偶爾笑也不是對著你笑,而是對著空氣笑,讓人恨的牙癢癢,他媽媽說他這是對
          花笑,“對花笑”這詞還真有點佛意在里面。現(xiàn)在三個月了,開始會對著你笑,不過有個問題,他很少對著抱他的人笑,反而不
          抱他的人逗他一下就咧開了小嘴笑嘻嘻。小毅同學(xué)的哭也很有趣,頭兩個月的哭是真哭,那種臉紅脖子粗、眼淚豆大豆大往下掉
          的哭,現(xiàn)在哭呢,就是把眼睛咪上,“扮”出一副委屈樣,嘴里哼著我稱之為咕嚕咕嚕語的聲音,可憐兮兮地看著你,等著你抱
          他;你要是不抱,他立馬“加大音量”,此時他爺爺奶奶就撐不住了,趕緊把他抱起來,如果是我,繼續(xù)不抱的話,他馬上開始
          掉眼淚了,此時才是“真哭”呀。


          posted @ 2009-03-27 09:11 dennis 閱讀(2749) | 評論 (2)編輯 收藏

               摘要:     受javaeye上的《Ruby中文編程》啟發(fā),帖子中有人提到如果if這樣的關(guān)鍵字都可以定義成中文,那就是真正的中文編程。那時我就想到,這個其實要在scheme中實現(xiàn)是多么簡單,將sicp書中的解釋器稍微修改下就可以了,只要修改解析的部分即可。解釋器的完整代碼放后面,我們先看看有趣的例子: Code highlighting produced by A...  閱讀全文

          posted @ 2009-03-20 23:27 dennis 閱讀(3123) | 評論 (8)編輯 收藏

          本圖不針對任何網(wǎng)站和個人,僅僅是因為很離譜。





          posted @ 2009-03-19 19:04 dennis 閱讀(1372) | 評論 (3)編輯 收藏

          僅列出標題
          共56頁: First 上一頁 17 18 19 20 21 22 23 24 25 下一頁 Last 
          主站蜘蛛池模板: 库伦旗| 武城县| 安岳县| 鲁甸县| 万州区| 凤庆县| 昌江| 溧阳市| 临沂市| 竹山县| 新化县| 老河口市| 柞水县| 青神县| 定陶县| 龙泉市| 含山县| 台北市| 双桥区| 沙洋县| 天津市| 余庆县| 泾阳县| 即墨市| 韩城市| 辽阳县| 丹棱县| 涿鹿县| 正阳县| 呼图壁县| 磐安县| 东海县| 宁波市| 呈贡县| 尼勒克县| 霸州市| 肃南| 通渭县| 曲靖市| 永兴县| 丰县|