我的Java路上那些事兒

          快樂(lè)成長(zhǎng)
          posts - 110, comments - 101, trackbacks - 0, articles - 7
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          public class JVMTest {

          public static void main(String[] args){
          System.out.println("aa:" + aa());
          }
          public static int aa(){
          int a = 1;
          int b = 10;
          try{
          System.out.println("abc");
          return a;
          }finally{
          a = 2;
          System.out.println("a: "+ a);
          }
          }
          }

          運(yùn)行結(jié)果為:

          abc
          a: 2
          aa:1

          由此可知:在try語(yǔ)句中,在執(zhí)行return語(yǔ)句時(shí),要返回的結(jié)果已經(jīng)準(zhǔn)備好了,就在此時(shí),程序轉(zhuǎn)到finally執(zhí)行了。

          在轉(zhuǎn)去之前,try中先把要返回的結(jié)果存放到不同于a的局部變量中去,執(zhí)行完finally之后,在從中取出返回結(jié)果,

          因此,即使finally中對(duì)變量a進(jìn)行了改變,但是不會(huì)影響返回結(jié)果。

          但是,如果在finally子句中最后添加上return a會(huì)怎樣呢?

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

          Compiling 1 source file to E:\sun\InsideJVM\build\classes
          E:\sun\InsideJVM\src\JVMTest.java:37: warning: finally clause cannot complete normally
          }
          1 warning
          compile-single:
          run-single:
          abc
          a: 2
          aa:2

          測(cè)試1
          public static int test1()
          {
          int i = 1;
          try
          {
          return ++i;
          }
          finally
          {
          ++i;
          Console.WriteLine("finally:" + i);
          }
          }

          static void Main(string[] args)
          {
          Console.WriteLine("Main:" + test1());
          }
          結(jié)果:
          finally:3
          Main:2

          測(cè)試2
          public static int test2()
          {
          int i = 1;
          try
          {
          throw new Exception();
          }
          catch
          {
          return ++i;
          }
          finally
          {
          ++i;
          Console.WriteLine("finally:" + i);
          }
          }

          static void Main(string[] args)
          {
          Console.WriteLine("Main:" + test2());
          }
          結(jié)果:
          finally:3
          Main:2

          測(cè)試3
          public static int test3()
          {
          try{}
          finally
          {
          return 1;
          }
          }

          結(jié)果:
          編譯錯(cuò)誤,控制不能離開 finally 子句主體。

          結(jié)論:

          1.不管出沒出現(xiàn)異常,finally塊中的語(yǔ)句都會(huì)執(zhí)行;
          2.當(dāng)trycatch塊中有return語(yǔ)句時(shí),finally塊中的語(yǔ)句仍會(huì)執(zhí)行;
          3.finally塊中的語(yǔ)句是在return語(yǔ)句執(zhí)行之后才執(zhí)行的,即函數(shù)返回值是在finally塊中語(yǔ)句執(zhí)行前確定的;
          4.finally塊中不能包含return語(yǔ)句。

          總結(jié):finallyreturn前執(zhí)行,在finally的操作,不會(huì)改變已經(jīng)確定的return的值,

          finally不能加return語(yǔ)句。出現(xiàn)異常,先找是否有處理器可以處理這個(gè)異常.finally。

          posted @ 2011-11-10 21:20 云云 閱讀(528) | 評(píng)論 (1)編輯 收藏

          這篇文章試驗(yàn)了JDK動(dòng)態(tài)代理與CGLIB動(dòng)態(tài)代理。從Spring的AOP框架介紹中得知對(duì)于使用接口的類,Spring使用JDK 動(dòng)態(tài)代理(原來(lái)做項(xiàng)目中試圖從Bean強(qiáng)制轉(zhuǎn)換為實(shí)現(xiàn)類,結(jié)果報(bào)錯(cuò),原來(lái)是這么回事),沒有接口的就使用別的AOP框架aspectj,但這些都是依賴于 Java字節(jié)碼工具ASM生成一個(gè)原類的新類,調(diào)用Callback

          但是JDK動(dòng)態(tài)代理為什么必須使用接口一直很疑惑,難道原理不是像ASM一樣修改字節(jié)碼嗎?帶著這個(gè)疑問(wèn),開始看JDK的Proxy代碼。使用JDK動(dòng)態(tài)代理的代碼代碼。

          Java代碼 復(fù)制代碼 收藏代碼
          1. ITestBean tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));


          于是從創(chuàng)建代理函數(shù)看起,即public static Object newProxyInstance(ClassLoader loader,
          Class<?>[] interfaces, InvocationHandler h)
          throws IllegalArgumentException ,

          通過(guò)源碼可以看到,這個(gè)類第一步生成一個(gè)代理類(注意,這里的參數(shù)就是接口列表),

          Java代碼 復(fù)制代碼 收藏代碼
          1. Class cl = getProxyClass(loader, interfaces);


          然后通過(guò)代理類找到構(gòu)造參數(shù)為InvocationHandler的構(gòu)造函數(shù)并生成一個(gè)新類。

          Java代碼 復(fù)制代碼 收藏代碼
          1. Constructor cons = cl.getConstructor(constructorParams);//這個(gè)有用,在后面細(xì)說(shuō)
          2. return (Object) cons.newInstance(new Object[] { h });


          接口起什么作用呢,于是又看getProxyClass方法的代碼,這個(gè)源碼很長(zhǎng),就不細(xì)說(shuō)了。大致分為三段:

          第一:驗(yàn)證

          第二:緩存創(chuàng)建新類的結(jié)構(gòu),如果創(chuàng)建過(guò),則直接返回。(注意:這里的KEY就是接口列表)

          第三:如果沒有創(chuàng)建過(guò),則創(chuàng)建新類

          創(chuàng)建代碼如下
          Java代碼 復(fù)制代碼 收藏代碼
          1. long num;
          2. //獲得代理類數(shù)字標(biāo)識(shí)
          3. synchronized (nextUniqueNumberLock) {
          4. num = nextUniqueNumber++;
          5. }
          6. //獲得創(chuàng)建新類的類名$Proxy,包名為接口包名,但需要注意的是,如果有兩個(gè)接口而且不在同一個(gè)包下,也會(huì)報(bào)錯(cuò)
          7. String proxyName = proxyPkg + proxyClassNamePrefix + num;
          8. //調(diào)用class處理文件生成類的字節(jié)碼,根據(jù)接口列表創(chuàng)建一個(gè)新類,這個(gè)類為代理類,
          9. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
          10. proxyName, interfaces);
          11. //通過(guò)JNI接口,將Class字節(jié)碼文件定義一個(gè)新類
          12. proxyClass = defineClass0(loader, proxyName,
          13. proxyClassFile, 0, proxyClassFile.length);



          根據(jù)前面的代碼Constructor cons = cl.getConstructor(constructorParams);

          可以猜測(cè)到接口創(chuàng)建的新類proxyClassFile 不管采用什么接口,都是以下結(jié)構(gòu)

          Java代碼 復(fù)制代碼 收藏代碼
          1. public class $Proxy1 extends Proxy implements 傳入的接口{
          2. }


          生成新類的看不到源代碼,不過(guò)猜測(cè)它的執(zhí)行原理很有可能是如果類是Proxy的子類,則調(diào)用InvocationHandler進(jìn)行方法的Invoke

          到現(xiàn)在大家都應(yīng)該明白了吧,JDK動(dòng)態(tài)代理的原理是根據(jù)定義好的規(guī)則,用傳入的接口創(chuàng)建一個(gè)新類,這就是為什么采用動(dòng)態(tài)代理時(shí)為什么只能用接口引用指向代理,而不能用傳入的類引用執(zhí)行動(dòng)態(tài)類。

          cglib采用的是用創(chuàng)建一個(gè)繼承實(shí)現(xiàn)類的子類,用asm庫(kù)動(dòng)態(tài)修改子類的代碼來(lái)實(shí)現(xiàn)的,所以可以用傳入的類引用執(zhí)行代理類

          JDK動(dòng)態(tài)代理與CGLIB對(duì)比如下:

          //JDK動(dòng)態(tài)代理測(cè)試代碼

          Java代碼 復(fù)制代碼 收藏代碼
          1. ITestBean tb = new TestBean();
          2. tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));//這句用接口引用指向,不會(huì)報(bào)錯(cuò)
          3. TestBean tmp = (TestBean) tb;//強(qiáng)制轉(zhuǎn)換為實(shí)現(xiàn)類,將拋出類強(qiáng)制轉(zhuǎn)換異常


          //CGLIB測(cè)試代碼
          Java代碼 復(fù)制代碼 收藏代碼
          1. TestProxy tp = new TestProxy();
          2. tb = (ITestBean) tp.getProxy(TestBean.class);
          3. tmp = (TeatBean) tb;//強(qiáng)制轉(zhuǎn)換為實(shí)現(xiàn)類,不會(huì)拋出異常

          補(bǔ)充說(shuō)明,如果在實(shí)現(xiàn)類中,接口定義的方法互相調(diào)用不會(huì)在調(diào)用InvocationHandler的invoke方法,JDK動(dòng)態(tài)代理應(yīng)該不是嵌入到Java的反射機(jī)制中,而是在反射機(jī)制上的一個(gè)調(diào)用。


          應(yīng)用舉例如下:

          JDK動(dòng)態(tài)代理的簡(jiǎn)單使用示例:


          Java代碼 復(fù)制代碼 收藏代碼
          1. package com.proxy;
          2. public class ForumServiceImpl implements ForumService{
          3. public void removeTopic(int topicId){
          4. System.out.println("模擬刪除記錄"+topicId);
          5. try{
          6. Thread.currentThread().sleep(20);
          7. }catch(Exception e){
          8. throw new RuntimeException(e);
          9. }
          10. }
          11. public void removeForum(int forumId){
          12. System.out.println("模擬刪除記錄"+forumId);
          13. try{
          14. Thread.currentThread().sleep(20);
          15. }catch(Exception e){
          16. throw new RuntimeException(e);
          17. }
          18. }
          19. }


          創(chuàng)建一個(gè)實(shí)現(xiàn)java.lang.reflect.InvocationHandler 接口的代理類,如:
          Java代碼 復(fù)制代碼 收藏代碼
          1. import java.lang.reflect.InvocationHandler;
          2. import java.lang.reflect.Method;
          3. public class PerformanceHandler implements InvocationHandler{
          4. private Object target; //要進(jìn)行代理的業(yè)務(wù)類的實(shí)例
          5. public PerformanceHandler(Object target){
          6. this.target = target;
          7. }
          8. //覆蓋java.lang.reflect.InvocationHandler的方法invoke()進(jìn)行織入(增強(qiáng))的操作
          9. //在實(shí)際應(yīng)用中, 這里會(huì)引用一個(gè)Intercepter類來(lái)做處理。 然后Intercepter就可以獨(dú)立發(fā)展
          10. public Object invoke(Object proxy, Method method, Object[] args)
          11. throws Throwable{
          12. System.out.println("Object target proxy:"+target);
          13. System.out.println("模擬代理加強(qiáng)的方法...");
          14. Object obj = method.invoke(target, args); //調(diào)用目標(biāo)業(yè)務(wù)類的方法
          15. System.out.println("模擬代理加強(qiáng)的方法執(zhí)行完畢...");
          16. return obj;
          17. }
          18. }



          用java.lang.reflect.Proxy.newProxyInstance()方法創(chuàng)建動(dòng)態(tài)實(shí)例來(lái)調(diào)用代理實(shí)例的方法:
          Java代碼 復(fù)制代碼 收藏代碼
          1. import java.lang.reflect.Proxy;
          2. public class TestForumService {
          3. public static void main(String args[]){
          4. ForumService target = new ForumServiceImpl();//要進(jìn)行代理的目標(biāo)業(yè)務(wù)類
          5. PerformanceHandler handler = new PerformanceHandler(target);//用代理類把目標(biāo)業(yè)務(wù)類進(jìn)行編織
          6. //創(chuàng)建代理實(shí)例,它可以看作是要代理的目標(biāo)業(yè)務(wù)類的加多了橫切代碼(方法)的一個(gè)子類
          7. ForumService proxy = (ForumService)Proxy.newProxyInstance(
          8. target.getClass().getClassLoader(),
          9. target.getClass().getInterfaces(), handler);
          10. proxy.removeForum(10);
          11. proxy.removeTopic(20);
          12. }
          13. }



          CGLib動(dòng)態(tài)代理示例:


          創(chuàng)建一個(gè)實(shí)現(xiàn)net.sf.cglib.proxy.MethodInterceptor接口的實(shí)例來(lái)為目標(biāo)業(yè)務(wù)類加入進(jìn)行代理時(shí)要進(jìn)行的操作或增強(qiáng):

          Java代碼 復(fù)制代碼 收藏代碼
          1. import java.lang.reflect.Method;
          2. import net.sf.cglib.proxy.MethodProxy;
          3. import net.sf.cglib.proxy.Enhancer;
          4. import net.sf.cglib.proxy.MethodInterceptor;
          5. /**
          6. *CGlib采用非常底層的字節(jié)碼技術(shù),可以為一個(gè)類創(chuàng)建子類,
          7. 并在子類中采用方法攔截技術(shù)攔截父類方法的調(diào)用,并順勢(shì)進(jìn)行增強(qiáng),即是織入橫切邏輯
          8. * @author tufu
          9. */
          10. public class CglibProxy implements MethodInterceptor{
          11. private Enhancer enhancer = new Enhancer();
          12. //覆蓋MethodInterceptor接口的getProxy()方法,設(shè)置
          13. public Object getProxy(Class clazz){
          14. enhancer.setSuperclass(clazz); //設(shè)者要?jiǎng)?chuàng)建子類的類
          15. enhancer.setCallback(this); //設(shè)置回調(diào)的對(duì)象
          16. return enhancer.create(); //通過(guò)字節(jié)碼技術(shù)動(dòng)態(tài)創(chuàng)建子類實(shí)例,
          17. }
          18. public Object intercept(Object obj,Method method,Object[] args,
          19. MethodProxy proxy) throws Throwable {
          20. System.out.println("模擬代理增強(qiáng)方法");
          21. //通過(guò)代理類實(shí)例調(diào)用父類的方法,即是目標(biāo)業(yè)務(wù)類方法的調(diào)用
          22. Object result = proxy.invokeSuper(obj, args);
          23. System.out.println("模擬代理增強(qiáng)方法結(jié)束");
          24. return result;
          25. }
          26. }


          通過(guò)java.lang.reflect.Proxy的getProxy()動(dòng)態(tài)生成目標(biāo)業(yè)務(wù)類的子類,即是代理類,再由此得到代理實(shí)例:
          Java代碼 復(fù)制代碼 收藏代碼
          1. import com.proxy.ForumServiceImpl;
          2. import java.lang.reflect.Proxy;
          3. public class TestCglibProxy {
          4. public static void main(String args[]){
          5. CglibProxy proxy = new CglibProxy();
          6. //動(dòng)態(tài)生成子類的方法創(chuàng)建代理類
          7. ForumServiceImpl fsi =
          8. (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
          9. fsi.removeForum(10);
          10. fsi.removeTopic(2);
          11. }
          12. }


          總結(jié)下Spring的AOP運(yùn)用的設(shè)計(jì)模式 , AOP 主要利用代理模式, 然后依賴通知(本人認(rèn)為是策略模式)來(lái)實(shí)現(xiàn)AOP。 這樣通知就可以獨(dú)立發(fā)展。

          posted @ 2011-11-09 23:30 云云 閱讀(5542) | 評(píng)論 (0)編輯 收藏

          數(shù)據(jù)庫(kù)提供了四種事務(wù)隔離級(jí)別, 不同的隔離級(jí)別采用不同的鎖類開來(lái)實(shí)現(xiàn).

          在四種隔離級(jí)別中, Serializable的級(jí)別最高, Read Uncommited級(jí)別最低.

          大多數(shù)數(shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別為: Read Commited,如Sql Server , Oracle.

          少數(shù)數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別為Repeatable Read, 如MySQL InnoDB存儲(chǔ)引擎

          即使是最低的級(jí)別,也不會(huì)出現(xiàn) 第一類 丟失 更新問(wèn)題 .

          1. 臟讀(事務(wù)沒提交,提前讀取):臟讀就是指當(dāng)一個(gè)事務(wù)正在訪問(wèn)數(shù)據(jù),并且對(duì)數(shù)據(jù)進(jìn)行了修改,而這種修改還沒有提交到數(shù)據(jù)庫(kù)中,這時(shí),另外一個(gè)事務(wù)也訪問(wèn)這個(gè)數(shù)據(jù),然后使用了這個(gè)數(shù)據(jù)。

          2. 不可重復(fù)讀(兩次讀的不一致) :是指在一個(gè)事務(wù)內(nèi),多次讀同一數(shù)據(jù)。在這個(gè)事務(wù)還沒有結(jié)束時(shí),另外一個(gè)事務(wù)也訪問(wèn)該同一數(shù)據(jù)。那么,在第一個(gè)事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個(gè)事務(wù)的修改,那么第一個(gè)事務(wù)兩次讀到的的數(shù)據(jù)可能是不一樣的。這樣就發(fā)生了在一個(gè)事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的,因此稱為是不可重復(fù)讀。例如,一個(gè)編輯人員兩次讀取同一文檔,但在兩次讀取之間,作者重寫了該文檔。當(dāng)編輯人員第二次讀取文檔時(shí),文檔已更改。原始讀取不可重復(fù)。如果只有在作者全部完成編寫后編輯人員才可以讀取文檔,則可以避免該問(wèn)題。
          3. 幻讀 : 是指當(dāng)事務(wù)不是獨(dú)立執(zhí)行時(shí)發(fā)生的一種現(xiàn)象,例如第一個(gè)事務(wù)對(duì)一個(gè)表中的數(shù)據(jù)進(jìn)行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。同時(shí),第二個(gè)事務(wù)也修改這個(gè)表中的數(shù)據(jù),這種修改是向表中插入一行新數(shù)據(jù)。那么,以后就會(huì)發(fā)生操作第一個(gè)事務(wù)的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣。例如,一個(gè)編輯人員更改作者提交的文檔,但當(dāng)生產(chǎn)部門將其更改內(nèi)容合并到該文檔的主復(fù)本時(shí),發(fā)現(xiàn)作者已將未編輯的新材料添加到該文檔中。如果在編輯人員和生產(chǎn)部門完成對(duì)原始文檔的處理之前,任何人都不能將新材料添加到文檔中,則可以避免該問(wèn)題。
          4.第一類更新丟失(回滾丟失)
          當(dāng)2個(gè)事務(wù)更新相同的數(shù)據(jù)源,如果第一個(gè)事務(wù)被提交,而另外一個(gè)事務(wù)卻被撤銷,那么會(huì)連同第一個(gè)事務(wù)所做的跟新也被撤銷。也就是說(shuō)第一個(gè)事務(wù)做的跟新丟失了。
          5.第二類更新丟失(覆蓋丟失)
          第二類更新丟失實(shí)在實(shí)際應(yīng)用中經(jīng)常遇到的并發(fā)問(wèn)題,他和不可重復(fù)讀本質(zhì)上是同一類并發(fā)問(wèn)題,通常他被看做不可重復(fù)讀的特例:當(dāng)2個(gè)或這個(gè)多個(gè)事務(wù)查詢同樣的記錄然后各自基于最初的查詢結(jié)果更新該行時(shí),會(huì)造成第二類丟失更新。因?yàn)槊總€(gè)事務(wù)都不知道不知道其他事務(wù)的存在,最后一個(gè)事務(wù)對(duì)記錄做的修改將覆蓋其他事務(wù)對(duì)該記錄做的已提交的跟新...
          補(bǔ)充 : 基于元數(shù)據(jù)的 Spring 聲明性事務(wù) :

          Isolation 屬性一共支持五種事務(wù)設(shè)置,具體介紹如下:

          l DEFAULT 使用數(shù)據(jù)庫(kù)設(shè)置的隔離級(jí)別 ( 默認(rèn) ) ,由 DBA 默認(rèn)的設(shè)置來(lái)決定隔離級(jí)別 .

          l READ_UNCOMMITTED 會(huì)出現(xiàn)臟讀、不可重復(fù)讀、幻讀 ( 隔離級(jí)別最低,并發(fā)性能高 )

          l READ_COMMITTED 會(huì)出現(xiàn)不可重復(fù)讀、幻讀問(wèn)題(鎖定正在讀取的行

          l REPEATABLE_READ 會(huì)出幻讀(鎖定所讀取的所有行

          l SERIALIZABLE 保證所有的情況不會(huì)發(fā)生(鎖表

          不可重復(fù)讀的重點(diǎn)是修改 :
          同樣的條件 , 你讀取過(guò)的數(shù)據(jù) , 再次讀取出來(lái)發(fā)現(xiàn)值不一樣了
          幻讀的重點(diǎn)在于新增或者刪除
          同樣的條件 , 第 1 次和第 2 次讀出來(lái)的記錄數(shù)不一樣

          posted @ 2011-11-09 20:53 云云 閱讀(1611) | 評(píng)論 (1)編輯 收藏

          面臨的問(wèn)題

          對(duì)于高并發(fā)高訪問(wèn)的Web應(yīng)用程序來(lái)說(shuō),數(shù)據(jù)庫(kù)存取瓶頸一直是個(gè)令人頭疼的問(wèn)題。特別當(dāng)你的程序架構(gòu)還是建立在單數(shù)據(jù)庫(kù)模式,而一個(gè)數(shù)據(jù)池連接數(shù)峰值已經(jīng)達(dá)到500的時(shí)候,那你的程序運(yùn)行離崩潰的邊緣也不遠(yuǎn)了。很多小網(wǎng)站的開發(fā)人員一開始都將注意力放在了產(chǎn)品需求設(shè)計(jì)上,缺忽視了程序整體性能,可擴(kuò)展性等方面的考慮,結(jié)果眼看著訪問(wèn)量一天天網(wǎng)上爬,可突然發(fā)現(xiàn)有一天網(wǎng)站因?yàn)樵L問(wèn)量過(guò)大而崩潰了,到時(shí)候哭都來(lái)不及。所以我們一定要未雨綢繆,在數(shù)據(jù)庫(kù)還沒罷工前,想方設(shè)法給它減負(fù),這也是這篇文章的主要議題。

          大家都知道,當(dāng)有一個(gè)request過(guò)來(lái)后,web服務(wù)器交給app服務(wù)器,app處理并從db中存取相關(guān)數(shù)據(jù),但db存取的花費(fèi)是相當(dāng)高昂的。特別是每次都取相同的數(shù)據(jù),等于是讓數(shù)據(jù)庫(kù)每次都在做高耗費(fèi)的無(wú)用功,數(shù)據(jù)庫(kù)如果會(huì)說(shuō)話,肯定會(huì)發(fā)牢騷,你都問(wèn)了這么多遍了,難道還記不住嗎?是啊,如果app拿到第一次數(shù)據(jù)并存到內(nèi)存里,下次讀取時(shí)直接從內(nèi)存里讀取,而不用麻煩數(shù)據(jù)庫(kù),這樣不就給數(shù)據(jù)庫(kù)減負(fù)了?而且從內(nèi)存取數(shù)據(jù)必然要比從數(shù)據(jù)庫(kù)媒介取快很多倍,反而提升了應(yīng)用程序的性能。

          因此,我們可以在web/app層與db層之間加一層cache層,主要目的:1. 減少數(shù)據(jù)庫(kù)讀取負(fù)擔(dān);2. 提高數(shù)據(jù)讀取速度。而且,cache存取的媒介是內(nèi)存,而一臺(tái)服務(wù)器的內(nèi)存容量一般都是有限制的,不像硬盤容量可以做到TB級(jí)別。所以,可以考慮采用分布式的cache層,這樣更易于破除內(nèi)存容量的限制,同時(shí)又增加了靈活性。

          Memcached 介紹

          Memcached是開源的分布式cache系統(tǒng),現(xiàn)在很多的大型web應(yīng)用程序包括facebook,youtube,wikipedia,yahoo等等都在使用memcached來(lái)支持他們每天數(shù)億級(jí)的頁(yè)面訪問(wèn)。通過(guò)把cache層與他們的web架構(gòu)集成,他們的應(yīng)用程序在提高了性能的同時(shí),還大大降低了數(shù)據(jù)庫(kù)的負(fù)載。
          具體的memcached資料大家可以直接從它的官方網(wǎng)站[1]上得到。這里我就簡(jiǎn)單給大家介紹一下memcached的工作原理:

          Memcached處理的原子是每一個(gè)(key,value)對(duì)(以下簡(jiǎn)稱kv對(duì)),key會(huì)通過(guò)一個(gè)hash算法轉(zhuǎn)化成hash-key,便于查找、對(duì)比以及做到盡可能的散列。同時(shí),memcached用的是一個(gè)二級(jí)散列,通過(guò)一張大hash表來(lái)維護(hù)。

          Memcached有兩個(gè)核心組件組成:服務(wù)端(ms)和客戶端(mc),在一個(gè)memcached的查詢中,mc先通過(guò)計(jì)算key的hash值來(lái)確定kv對(duì)所處在的ms位置。當(dāng)ms確定后,客戶端就會(huì)發(fā)送一個(gè)查詢請(qǐng)求給對(duì)應(yīng)的ms,讓它來(lái)查找確切的數(shù)據(jù)。因?yàn)檫@之間沒有交互以及多播協(xié)議,所以memcached交互帶給網(wǎng)絡(luò)的影響是最小化的。

          舉例說(shuō)明:考慮以下這個(gè)場(chǎng)景,有三個(gè)mc分別是X,Y,Z,還有三個(gè)ms分別是A,B,C:

          設(shè)置kv對(duì)
          X想設(shè)置key=”foo”,value=”seattle”
          X拿到ms列表,并對(duì)key做hash轉(zhuǎn)化,根據(jù)hash值確定kv對(duì)所存的ms位置
          B被選中了
          X連接上B,B收到請(qǐng)求,把(key=”foo”,value=”seattle”)存了起來(lái)

          獲取kv對(duì)
          Z想得到key=”foo”的value
          Z用相同的hash算法算出hash值,并確定key=”foo”的值存在B上
          Z連接上B,并從B那邊得到value=”seattle”
          其他任何從X,Y,Z的想得到key=”foo”的值的請(qǐng)求都會(huì)發(fā)向B

          Memcached服務(wù)器(ms)

          內(nèi)存分配

          默認(rèn)情況下,ms是用一個(gè)內(nèi)置的叫“塊分配器”的組件來(lái)分配內(nèi)存的。舍棄c++標(biāo)準(zhǔn)的malloc/free的內(nèi)存分配,而采用塊分配器的主要目的是為了避免內(nèi)存碎片,否則操作系統(tǒng)要花費(fèi)更多時(shí)間來(lái)查找這些邏輯上連續(xù)的內(nèi)存塊(實(shí)際上是斷開的)。用了塊分配器,ms會(huì)輪流的對(duì)內(nèi)存進(jìn)行大塊的分配,并不斷重用。當(dāng)然由于塊的大小各不相同,當(dāng)數(shù)據(jù)大小和塊大小不太相符的情況下,還是有可能導(dǎo)致內(nèi)存的浪費(fèi)。

          同時(shí),ms對(duì)key和data都有相應(yīng)的限制,key的長(zhǎng)度不能超過(guò)250字節(jié),data也不能超過(guò)塊大小的限制 --- 1MB。
          因?yàn)閙c所使用的hash算法,并不會(huì)考慮到每個(gè)ms的內(nèi)存大小。理論上mc會(huì)分配概率上等量的kv對(duì)給每個(gè)ms,這樣如果每個(gè)ms的內(nèi)存都不太一樣,那可能會(huì)導(dǎo)致內(nèi)存使用率的降低。所以一種替代的解決方案是,根據(jù)每個(gè)ms的內(nèi)存大小,找出他們的最大公約數(shù),然后在每個(gè)ms上開n個(gè)容量=最大公約數(shù)的instance,這樣就等于擁有了多個(gè)容量大小一樣的子ms,從而提供整體的內(nèi)存使用率。

          緩存策略

          當(dāng)ms的hash表滿了之后,新的插入數(shù)據(jù)會(huì)替代老的數(shù)據(jù),更新的策略是LRU(最近最少使用),以及每個(gè)kv對(duì)的有效時(shí)限。Kv對(duì)存儲(chǔ)有效時(shí)限是在mc端由app設(shè)置并作為參數(shù)傳給ms的。

          同時(shí)ms采用是偷懶替代法,ms不會(huì)開額外的進(jìn)程來(lái)實(shí)時(shí)監(jiān)測(cè)過(guò)時(shí)的kv對(duì)并刪除,而是當(dāng)且僅當(dāng),新來(lái)一個(gè)插入的數(shù)據(jù),而此時(shí)又沒有多余的空間放了,才會(huì)進(jìn)行清除動(dòng)作。

          緩存數(shù)據(jù)庫(kù)查詢
          現(xiàn)在memcached最流行的一種使用方式是緩存數(shù)據(jù)庫(kù)查詢,下面舉一個(gè)簡(jiǎn)單例子說(shuō)明:

          App需要得到userid=xxx的用戶信息,對(duì)應(yīng)的查詢語(yǔ)句類似:

          “SELECT * FROM users WHERE userid = xxx”

          App先去問(wèn)cache,有沒有“user:userid”(key定義可預(yù)先定義約束好)的數(shù)據(jù),如果有,返回?cái)?shù)據(jù);如果沒有,App會(huì)從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù),并調(diào)用cache的add函數(shù),把數(shù)據(jù)加入cache中。

          當(dāng)取的數(shù)據(jù)需要更新,app會(huì)調(diào)用cache的update函數(shù),來(lái)保持?jǐn)?shù)據(jù)庫(kù)與cache的數(shù)據(jù)同步。

          從上面的例子我們也可以發(fā)現(xiàn),一旦數(shù)據(jù)庫(kù)的數(shù)據(jù)發(fā)現(xiàn)變化,我們一定要及時(shí)更新cache中的數(shù)據(jù),來(lái)保證app讀到的是同步的正確數(shù)據(jù)。當(dāng)然我們可以通過(guò)定時(shí)器方式記錄下cache中數(shù)據(jù)的失效時(shí)間,時(shí)間一過(guò)就會(huì)激發(fā)事件對(duì)cache進(jìn)行更新,但這之間總會(huì)有時(shí)間上的延遲,導(dǎo)致app可能從cache讀到臟數(shù)據(jù),這也被稱為狗洞問(wèn)題。(以后我會(huì)專門描述研究這個(gè)問(wèn)題)

          數(shù)據(jù)冗余與故障預(yù)防

          從設(shè)計(jì)角度上,memcached是沒有數(shù)據(jù)冗余環(huán)節(jié)的,它本身就是一個(gè)大規(guī)模的高性能cache層,加入數(shù)據(jù)冗余所能帶來(lái)的只有設(shè)計(jì)的復(fù)雜性和提高系統(tǒng)的開支。

          當(dāng)一個(gè)ms上丟失了數(shù)據(jù)之后,app還是可以從數(shù)據(jù)庫(kù)中取得數(shù)據(jù)。不過(guò)更謹(jǐn)慎的做法是在某些ms不能正常工作時(shí),提供額外的ms來(lái)支持cache,這樣就不會(huì)因?yàn)閍pp從cache中取不到數(shù)據(jù)而一下子給數(shù)據(jù)庫(kù)帶來(lái)過(guò)大的負(fù)載。

          同時(shí)為了減少某臺(tái)ms故障所帶來(lái)的影響,可以使用“熱備份”方案,就是用一臺(tái)新的ms來(lái)取代有問(wèn)題的ms,當(dāng)然新的ms還是要用原來(lái)ms的IP地址,大不了數(shù)據(jù)重新裝載一遍。

          另外一種方式,就是提高你ms的節(jié)點(diǎn)數(shù),然后mc會(huì)實(shí)時(shí)偵查每個(gè)節(jié)點(diǎn)的狀態(tài),如果發(fā)現(xiàn)某個(gè)節(jié)點(diǎn)長(zhǎng)時(shí)間沒有響應(yīng),就會(huì)從mc的可用server列表里刪除,并對(duì)server節(jié)點(diǎn)進(jìn)行重新hash定位。當(dāng)然這樣也會(huì)造成的問(wèn)題是,原本key存儲(chǔ)在B上,變成存儲(chǔ)在C上了。所以此方案本身也有其弱點(diǎn),最好能和“熱備份”方案結(jié)合使用,就可以使故障造成的影響最小化。

          Memcached客戶端(mc)

          Memcached客戶端有各種語(yǔ)言的版本供大家使用,包括java,c,php,.net等等,具體可參見memcached api page[2]。
          大家可以根據(jù)自己項(xiàng)目的需要,選擇合適的客戶端來(lái)集成。

          緩存式的Web應(yīng)用程序架構(gòu)
          有了緩存的支持,我們可以在傳統(tǒng)的app層和db層之間加入cache層,每個(gè)app服務(wù)器都可以綁定一個(gè)mc,每次數(shù)據(jù)的讀取都可以從ms中取得,如果沒有,再?gòu)膁b層讀取。而當(dāng)數(shù)據(jù)要進(jìn)行更新時(shí),除了要發(fā)送update的sql給db層,同時(shí)也要將更新的數(shù)據(jù)發(fā)給mc,讓mc去更新ms中的數(shù)據(jù)。

          假設(shè)今后我們的數(shù)據(jù)庫(kù)可以和ms進(jìn)行通訊了,那可以將更新的任務(wù)統(tǒng)一交給db層,每次數(shù)據(jù)庫(kù)更新數(shù)據(jù)的同時(shí)會(huì)自動(dòng)去更新ms中的數(shù)據(jù),這樣就可以進(jìn)一步減少app層的邏輯復(fù)雜度。如下圖:

          不過(guò)每次我們?nèi)绻麤]有從cache讀到數(shù)據(jù),都不得不麻煩數(shù)據(jù)庫(kù)。為了最小化數(shù)據(jù)庫(kù)的負(fù)載壓力,我們可以部署數(shù)據(jù)庫(kù)復(fù)寫,用slave數(shù)據(jù)庫(kù)來(lái)完成讀取操作,而master數(shù)據(jù)庫(kù)永遠(yuǎn)只負(fù)責(zé)三件事:1.更新數(shù)據(jù);2.同步slave數(shù)據(jù)庫(kù);3.更新cache。如下圖:

          以上這些緩存式web架構(gòu)在實(shí)際應(yīng)用中被證明是能有效并能極大地降低數(shù)據(jù)庫(kù)的負(fù)載同時(shí)又能提高web的運(yùn)行性能。當(dāng)然這些架構(gòu)還可以根據(jù)具體的應(yīng)用環(huán)境進(jìn)行變種,以達(dá)到不同硬件條件下性能的最優(yōu)化。

          未來(lái)的憧憬

          posted @ 2011-11-08 21:51 云云 閱讀(812) | 評(píng)論 (0)編輯 收藏

               摘要: 1. 基本 概念 IO 是主存和外部設(shè)備 ( 硬盤、終端和網(wǎng)絡(luò)等 ) 拷貝數(shù)據(jù)的過(guò)程。 IO 是操作系統(tǒng)的底層功能實(shí)現(xiàn),底層通過(guò) I/O 指令進(jìn)行完成。 所有語(yǔ)言運(yùn)行時(shí)系統(tǒng)提供執(zhí)行 I/O 較高級(jí)別的工具。 (c 的 printf scanf,java 的面向?qū)ο蠓庋b ) 2. Java 標(biāo)準(zhǔn) io 回顧 Java 標(biāo)準(zhǔn) IO 類庫(kù)是 io 面向?qū)ο蟮囊环N抽象?;诒镜胤椒ǖ牡讓訉?shí)...  閱讀全文

          posted @ 2011-11-08 21:25 云云 閱讀(1436) | 評(píng)論 (0)編輯 收藏

          class NewThread implements Runnable {
          Thread t;
          public NewThread() {
          t = new Thread(this,"Demo thread");
          System.out.println("Child thread : " + t);
          t.run();
          }
          public void run(){
          try{
          for( int i = 5; i > 0; i --){
          System.out.println("Child thread :" + i);
          Thread.sleep(500);
          }

          }catch(InterruptedException e){
          System.out.println("Child interrupted.");
          }
          System.out.println("Exiting child thread.");

          }
          }

          public class TestDemo{
          public static void main(String args[]){
          new NewThread();
          try{
          for( int i = 5; i > 0; i --){
          System.out.println("Main thread :" + i);
          Thread.sleep(1000);
          }
          }catch(InterruptedException e){
          System.out.println("Main interrupted.");
          }
          System.out.println("Exiting Main thread.");
          }
          }

          這是一個(gè)實(shí)現(xiàn)多線程的程序,運(yùn)行結(jié)果如下:
          Child thread : Thread[Demo thread,5,main]
          Main thread :5
          Child thread :5
          Child thread :4
          Main thread :4
          Child thread :3
          Child thread :2
          Main thread :3
          Child thread :1
          Exiting child thread.
          Main thread :2
          Main thread :1
          Exiting Main thread.

          試想,如果把 start()改成run()會(huì)出現(xiàn)什么結(jié)果?
          修改之后運(yùn)行結(jié)果:
          Child thread : Thread[Demo thread,5,main]
          Child thread :5
          Child thread :4
          Child thread :3
          Child thread :2
          Child thread :1
          Exiting child thread.
          Main thread :5
          Main thread :4
          Main thread :3
          Main thread :2
          Main thread :1
          Exiting Main thread.
          程序運(yùn)行成為了單線程順序執(zhí)行。為什么?
          start方法:用來(lái)啟動(dòng)一個(gè)線程, 這時(shí)此線程是處于就緒狀態(tài), 并沒有運(yùn)行。 然后通過(guò)此Thread類調(diào)用方法run()來(lái)完成其運(yùn)行操作的, 這里方法run()稱為線程體, 它包含了要執(zhí)行的這個(gè)線程的內(nèi)容, run方法運(yùn)行結(jié)束, 此線程終止, 而CPU再運(yùn)行其它線程,
          直接用run方法: 這只是調(diào)用一個(gè)方法而已, 程序中依然只有主線程--這一個(gè)線程, 其程序執(zhí)行路徑還是只有一條, 這樣就沒有達(dá)到寫線程的目的。
          記?。壕€程就是為了更好地利用CPU,提高程序運(yùn)行速率的!

          posted @ 2011-11-08 20:53 云云 閱讀(2799) | 評(píng)論 (0)編輯 收藏

          在java中可有兩種方式實(shí)現(xiàn)多線程,一種是繼承Thread類,一種是實(shí)現(xiàn)Runnable接口;Thread類是在java.lang包中定義的。一個(gè)類只要繼承了Thread類同時(shí)覆寫了本類中的run()方法就可以實(shí)現(xiàn)多線程操作了,但是一個(gè)類只能繼承一個(gè)父類,這是此方法的局限,下面看例子:
          package org.thread.demo;
          class MyThread extends Thread{
          private String name;
          public MyThread(String name) {
          super();
          this.name = name;
          }
          public void run(){
          for(int i=0;i<10;i++){
          System.out.println("線程開始:"+this.name+",i="+i);
          }
          }
          }

          package org.thread.demo;
          public class ThreadDemo01 {
          public static void main(String[] args) {
          MyThread mt1=new MyThread("線程a");
          MyThread mt2=new MyThread("線程b");
          mt1.run();
          mt2.run();
          }
          }
          但是,此時(shí)結(jié)果很有規(guī)律,先第一個(gè)對(duì)象執(zhí)行,然后第二個(gè)對(duì)象執(zhí)行,并沒有相互運(yùn)行。在JDK的文檔中可以發(fā)現(xiàn),一旦調(diào)用start()方法,則會(huì)通過(guò) JVM找到run()方法。下面啟動(dòng)
          start()方法啟動(dòng)線程:
          package org.thread.demo;
          public class ThreadDemo01 {
          public static void main(String[] args) {
          MyThread mt1=new MyThread("線程a");
          MyThread mt2=new MyThread("線程b");
          mt1.start();
          mt2.start();
          }
          };

          這樣程序可以正常完成交互式運(yùn)行。那么為啥非要使用start();方法啟動(dòng)多線程呢?
          在JDK的安裝路徑下,src.zip是全部的java源程序,通過(guò)此代碼找到Thread中的start()方法的定義,可以發(fā)現(xiàn)此方法中使用了 private native void start0();其中native關(guān)鍵字表示可以調(diào)用操作系統(tǒng)的底層函數(shù),那么這樣的技術(shù)成為JNI技術(shù)(java Native Interface)

          Runnable接口
          在實(shí)際開發(fā)中一個(gè)多線程的操作很少使用Thread類,而是通過(guò)Runnable接口完成。
          public interface Runnable{
          public void run();
          }
          例子:
          package org.runnable.demo;
          class MyThread implements Runnable{
          private String name;
          public MyThread(String name) {
          this.name = name;
          }
          public void run(){
          for(int i=0;i<100;i++){
          System.out.println("線程開始:"+this.name+",i="+i);
          }
          }
          };
          但是在使用Runnable定義的子類中沒有start()方法,只有Thread類中才有。此時(shí)觀察Thread類,有一個(gè)構(gòu)造方法:public Thread(Runnable targer)
          此構(gòu)造方法接受Runnable的子類實(shí)例,也就是說(shuō)可以通過(guò)Thread類來(lái)啟動(dòng)Runnable實(shí)現(xiàn)的多線程。(start()可以協(xié)調(diào)系統(tǒng)的資源):
          package org.runnable.demo;
          import org.runnable.demo.MyThread;
          public class ThreadDemo01 {
          public static void main(String[] args) {
          MyThread mt1=new MyThread("線程a");
          MyThread mt2=new MyThread("線程b");
          new Thread(mt1).start();
          new Thread(mt2).start();
          }
          }
          兩種實(shí)現(xiàn)方式的區(qū)別和聯(lián)系:
          在程序開發(fā)中只要是多線程肯定永遠(yuǎn)以實(shí)現(xiàn)Runnable接口為主,因?yàn)閷?shí)現(xiàn)Runnable接口相比繼承Thread類有如下好處:
          ->避免點(diǎn)繼承的局限,一個(gè)類可以繼承多個(gè)接口。
          ->適合于資源的共享
          以賣票程序?yàn)槔?,通過(guò)Thread類完成:
          package org.demo.dff;
          class MyThread extends Thread{
          private int ticket=10;
          public void run(){
          for(int i=0;i<20;i++){
          if(this.ticket>0){
          System.out.println("賣票:ticket"+this.ticket--);
          }
          }
          }
          };
          下面通過(guò)三個(gè)線程對(duì)象,同時(shí)賣票:
          package org.demo.dff;
          public class ThreadTicket {
          public static void main(String[] args) {
          MyThread mt1=new MyThread();
          MyThread mt2=new MyThread();
          MyThread mt3=new MyThread();
          mt1.start();//每個(gè)線程都各賣了10張,共賣了30張票
          mt2.start();//但實(shí)際只有10張票,每個(gè)線程都賣自己的票
          mt3.start();//沒有達(dá)到資源共享
          }
          }
          如果用Runnable就可以實(shí)現(xiàn)資源共享,下面看例子:
          package org.demo.runnable;
          class MyThread implements Runnable{
          private int ticket=10;
          public void run(){
          for(int i=0;i<20;i++){
          if(this.ticket>0){
          System.out.println("賣票:ticket"+this.ticket--);
          }
          }
          }
          }
          package org.demo.runnable;
          public class RunnableTicket {
          public static void main(String[] args) {
          MyThread mt=new MyThread();
          new Thread(mt).start();//同一個(gè)mt,但是在Thread中就不可以,如果用同一
          new Thread(mt).start();//個(gè)實(shí)例化對(duì)象mt,就會(huì)出現(xiàn)異常
          new Thread(mt).start();
          }
          };
          雖然現(xiàn)在程序中有三個(gè)線程,但是一共賣了10張票,也就是說(shuō)使用Runnable實(shí)現(xiàn)多線程可以達(dá)到資源共享目的。

          Runnable接口和Thread之間的聯(lián)系:
          public class Thread extends Object implements Runnable
          發(fā)現(xiàn)Thread類也是Runnable接口的子類。

          因?yàn)橐粋€(gè)線程只能啟動(dòng)一次,通過(guò)Thread實(shí)現(xiàn)線程時(shí),線程和線程所要執(zhí)行的任務(wù)是捆綁在一起的。也就使得一個(gè)任務(wù)只能啟動(dòng)一個(gè)線程,不同的線程執(zhí)行的任務(wù)是不相同的,所以沒有必要,也不能讓兩個(gè)線程共享彼此任務(wù)中的資源。

          一個(gè)任務(wù)可以啟動(dòng)多個(gè)線程,通過(guò)Runnable方式實(shí)現(xiàn)的線程,實(shí)際是開辟一個(gè)線程,將任務(wù)傳遞進(jìn)去,由此線程執(zhí)行。可以實(shí)例化多個(gè) Thread對(duì)象,將同一任務(wù)傳遞進(jìn)去,也就是一個(gè)任務(wù)可以啟動(dòng)多個(gè)線程來(lái)執(zhí)行它。這些線程執(zhí)行的是同一個(gè)任務(wù),所以他們的資源是共享。

          兩種不同的線程實(shí)現(xiàn)方式本身就決定了其是否能進(jìn)行資源共享。


          posted @ 2011-11-08 20:46 云云 閱讀(643) | 評(píng)論 (1)編輯 收藏

               摘要:  Spring AOP: Spring之面向方面編程 5.1. 概念 面向方面編程 (AOP) 提供從另一個(gè)角度來(lái)考慮程序結(jié)構(gòu)以完善面向?qū)ο缶幊蹋∣OP)。 面向?qū)ο髮?yīng)用程序分解成 各個(gè)層次的對(duì)象,而AOP將程序分解成各個(gè)方面 或者說(shuō) 關(guān)注點(diǎn) 。 這使得可以模塊化諸如事務(wù)管理等這些橫切多個(gè)對(duì)象的關(guān)注點(diǎn)。(這些關(guān)注點(diǎn)術(shù)語(yǔ)稱作 橫切關(guān)注點(diǎn)。) Spring的一...  閱讀全文

          posted @ 2011-11-08 20:35 云云 閱讀(9759) | 評(píng)論 (0)編輯 收藏

               摘要: Java里有個(gè)很重要的特色是Exception ,也就是說(shuō)允許程序產(chǎn)生例外狀況。而在學(xué)Java 的時(shí)候,我們也只知道Exception 的寫法,卻未必真能了解不同種類的Exception 的區(qū)別。   首先,您應(yīng)該知道的是Java 提供了兩種Exception 的模式,一種是執(zhí)行的時(shí)候所產(chǎn)生的Exception (Runtime Exception),另外一種則是受控制的Exception (...  閱讀全文

          posted @ 2011-11-07 22:49 云云 閱讀(559) | 評(píng)論 (0)編輯 收藏

          Comparable & Comparator 都是用來(lái)實(shí)現(xiàn)集合中的排序的,只是 Comparable 是在集合內(nèi)部定義的方法實(shí)現(xiàn)的排序,
          Comparator 是在集合外部實(shí)現(xiàn)的排序,所以,如想實(shí)現(xiàn)排序,就需要在集合外定義 Comparator 接口的方法或在集合內(nèi)實(shí)現(xiàn) Comparable 接口的方法。
          Comparable 是一個(gè)對(duì)象本身就已經(jīng)支持自比較所需要實(shí)現(xiàn)的接口(如 String、Integer 自己就可以完成比較大小操作)

          而 Comparator 是一個(gè)專用的比較器,當(dāng)這個(gè)對(duì)象不支持自比較或者自比較函數(shù)不能滿足你的要求時(shí),你可以寫一個(gè)比較器來(lái)完成兩個(gè)對(duì)象之間大小的比較。

            可以說(shuō)一個(gè)是自己完成比較,一個(gè)是外部程序?qū)崿F(xiàn)比較的差別而已。

            用 Comparator 是策略模式(strategy design pattern),就是不改變對(duì)象自身,而用一個(gè)策略對(duì)象(strategy object)來(lái)改變它的行為。

            比如:你想對(duì)整數(shù)采用絕對(duì)值大小來(lái)排序,Integer 是不符合要求的,你不需要去修改 Integer 類(實(shí)際上你也不能這么做)去改變它的排序行為,只要使用一個(gè)實(shí)現(xiàn)了 Comparator 接口的對(duì)象來(lái)實(shí)現(xiàn)控制它的排序就行了。
          Comparator 在java.util包中
          Comparable 在java.lang包中

            

           1public class TestComparator {
           2    AsComparator cl=new AsComparator();
           3    /**
           4     * @param args
           5     */
            
           6    @SuppressWarnings("unchecked")
           7    public static void main(String[] args) {
           8         Integer[] datas=new Integer[20];
           9         Random rand=new Random();
          10         for(int i=0;i<20;i++){
          11             datas[i]=new Integer(rand.nextInt(100));
          12         }

          13         Arrays.sort(datas);
          14         System.out.println(Arrays.asList(datas));
          15         TestComparator test=new TestComparator();
          16         Arrays.sort(datas,test.cl);
          17         System.out.println(Arrays.asList(datas));
          18         
          19    }

          20
          21    @SuppressWarnings("rawtypes")
          22    class AsComparator implements Comparator{
          23
          24        public int compare(Object o1, Object o2) {
          25             int value1= Math.abs(((Integer)o1).intValue());
          26             int value2=Math.abs(((Integer)o2).intValue());
          27             return value1>value2?1:(value1==value2?0:-1);
          28        }

          29        
          30    }

          31     
          32    
          33}

          34

           

          posted @ 2011-11-07 11:19 云云 閱讀(3144) | 評(píng)論 (0)編輯 收藏

          僅列出標(biāo)題
          共12頁(yè): First 上一頁(yè) 4 5 6 7 8 9 10 11 12 下一頁(yè) 
          主站蜘蛛池模板: 于都县| 上虞市| 沙河市| 孟州市| 叶城县| 高安市| 克拉玛依市| 三都| 邛崃市| 法库县| 阳谷县| 夹江县| 库伦旗| 深州市| 安福县| 辉南县| 阿巴嘎旗| 崇明县| 聊城市| 青浦区| 太谷县| 清涧县| 禹城市| 锡林浩特市| 江源县| 万山特区| 淅川县| 中超| 中西区| 鄄城县| 威信县| 安丘市| 府谷县| 阿尔山市| 乃东县| 青铜峡市| 汉沽区| 绥中县| 黑龙江省| 宁化县| 肥城市|