<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 110, comments - 101, trackbacks - 0, articles - 7
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    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);
    }
    }
    }

    運行結果為:

    abc
    a: 2
    aa:1

    由此可知:在try語句中,在執行return語句時,要返回的結果已經準備好了,就在此時,程序轉到finally執行了。

    在轉去之前,try中先把要返回的結果存放到不同于a的局部變量中去,執行完finally之后,在從中取出返回結果,

    因此,即使finally中對變量a進行了改變,但是不會影響返回結果。

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

    執行結果如下:

    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

    測試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());
    }
    結果:
    finally:3
    Main:2

    測試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());
    }
    結果:
    finally:3
    Main:2

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

    結果:
    編譯錯誤,控制不能離開 finally 子句主體。

    結論:

    1.不管出沒出現異常,finally塊中的語句都會執行;
    2.當trycatch塊中有return語句時,finally塊中的語句仍會執行;
    3.finally塊中的語句是在return語句執行之后才執行的,即函數返回值是在finally塊中語句執行前確定的;
    4.finally塊中不能包含return語句。

    總結:finallyreturn前執行,在finally的操作,不會改變已經確定的return的值,

    finally不能加return語句。出現異常,先找是否有處理器可以處理這個異常.finally。

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

    這篇文章試驗了JDK動態代理與CGLIB動態代理。從Spring的AOP框架介紹中得知對于使用接口的類,Spring使用JDK 動態代理(原來做項目中試圖從Bean強制轉換為實現類,結果報錯,原來是這么回事),沒有接口的就使用別的AOP框架aspectj,但這些都是依賴于 Java字節碼工具ASM生成一個原類的新類,調用Callback

    但是JDK動態代理為什么必須使用接口一直很疑惑,難道原理不是像ASM一樣修改字節碼嗎?帶著這個疑問,開始看JDK的Proxy代碼。使用JDK動態代理的代碼代碼。

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


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

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

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


    然后通過代理類找到構造參數為InvocationHandler的構造函數并生成一個新類。

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


    接口起什么作用呢,于是又看getProxyClass方法的代碼,這個源碼很長,就不細說了。大致分為三段:

    第一:驗證

    第二:緩存創建新類的結構,如果創建過,則直接返回。(注意:這里的KEY就是接口列表)

    第三:如果沒有創建過,則創建新類

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



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

    可以猜測到接口創建的新類proxyClassFile 不管采用什么接口,都是以下結構

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


    生成新類的看不到源代碼,不過猜測它的執行原理很有可能是如果類是Proxy的子類,則調用InvocationHandler進行方法的Invoke

    到現在大家都應該明白了吧,JDK動態代理的原理是根據定義好的規則,用傳入的接口創建一個新類,這就是為什么采用動態代理時為什么只能用接口引用指向代理,而不能用傳入的類引用執行動態類。

    cglib采用的是用創建一個繼承實現類的子類,用asm庫動態修改子類的代碼來實現的,所以可以用傳入的類引用執行代理類

    JDK動態代理與CGLIB對比如下:

    //JDK動態代理測試代碼

    Java代碼 復制代碼 收藏代碼
    1. ITestBean tb = new TestBean();
    2. tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));//這句用接口引用指向,不會報錯
    3. TestBean tmp = (TestBean) tb;//強制轉換為實現類,將拋出類強制轉換異常


    //CGLIB測試代碼
    Java代碼 復制代碼 收藏代碼
    1. TestProxy tp = new TestProxy();
    2. tb = (ITestBean) tp.getProxy(TestBean.class);
    3. tmp = (TeatBean) tb;//強制轉換為實現類,不會拋出異常

    補充說明,如果在實現類中,接口定義的方法互相調用不會在調用InvocationHandler的invoke方法,JDK動態代理應該不是嵌入到Java的反射機制中,而是在反射機制上的一個調用。


    應用舉例如下:

    JDK動態代理的簡單使用示例:


    Java代碼 復制代碼 收藏代碼
    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. }


    創建一個實現java.lang.reflect.InvocationHandler 接口的代理類,如:
    Java代碼 復制代碼 收藏代碼
    1. import java.lang.reflect.InvocationHandler;
    2. import java.lang.reflect.Method;
    3. public class PerformanceHandler implements InvocationHandler{
    4. private Object target; //要進行代理的業務類的實例
    5. public PerformanceHandler(Object target){
    6. this.target = target;
    7. }
    8. //覆蓋java.lang.reflect.InvocationHandler的方法invoke()進行織入(增強)的操作
    9. //在實際應用中, 這里會引用一個Intercepter類來做處理。 然后Intercepter就可以獨立發展
    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("模擬代理加強的方法...");
    14. Object obj = method.invoke(target, args); //調用目標業務類的方法
    15. System.out.println("模擬代理加強的方法執行完畢...");
    16. return obj;
    17. }
    18. }



    用java.lang.reflect.Proxy.newProxyInstance()方法創建動態實例來調用代理實例的方法:
    Java代碼 復制代碼 收藏代碼
    1. import java.lang.reflect.Proxy;
    2. public class TestForumService {
    3. public static void main(String args[]){
    4. ForumService target = new ForumServiceImpl();//要進行代理的目標業務類
    5. PerformanceHandler handler = new PerformanceHandler(target);//用代理類把目標業務類進行編織
    6. //創建代理實例,它可以看作是要代理的目標業務類的加多了橫切代碼(方法)的一個子類
    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動態代理示例:


    創建一個實現net.sf.cglib.proxy.MethodInterceptor接口的實例來為目標業務類加入進行代理時要進行的操作或增強:

    Java代碼 復制代碼 收藏代碼
    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采用非常底層的字節碼技術,可以為一個類創建子類,
    7. 并在子類中采用方法攔截技術攔截父類方法的調用,并順勢進行增強,即是織入橫切邏輯
    8. * @author tufu
    9. */
    10. public class CglibProxy implements MethodInterceptor{
    11. private Enhancer enhancer = new Enhancer();
    12. //覆蓋MethodInterceptor接口的getProxy()方法,設置
    13. public Object getProxy(Class clazz){
    14. enhancer.setSuperclass(clazz); //設者要創建子類的類
    15. enhancer.setCallback(this); //設置回調的對象
    16. return enhancer.create(); //通過字節碼技術動態創建子類實例,
    17. }
    18. public Object intercept(Object obj,Method method,Object[] args,
    19. MethodProxy proxy) throws Throwable {
    20. System.out.println("模擬代理增強方法");
    21. //通過代理類實例調用父類的方法,即是目標業務類方法的調用
    22. Object result = proxy.invokeSuper(obj, args);
    23. System.out.println("模擬代理增強方法結束");
    24. return result;
    25. }
    26. }


    通過java.lang.reflect.Proxy的getProxy()動態生成目標業務類的子類,即是代理類,再由此得到代理實例:
    Java代碼 復制代碼 收藏代碼
    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. //動態生成子類的方法創建代理類
    7. ForumServiceImpl fsi =
    8. (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
    9. fsi.removeForum(10);
    10. fsi.removeTopic(2);
    11. }
    12. }


    總結下Spring的AOP運用的設計模式 , AOP 主要利用代理模式, 然后依賴通知(本人認為是策略模式)來實現AOP。 這樣通知就可以獨立發展。

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

    數據庫提供了四種事務隔離級別, 不同的隔離級別采用不同的鎖類開來實現.

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

    大多數數據庫的默認隔離級別為: Read Commited,如Sql Server , Oracle.

    少數數據庫默認的隔離級別為Repeatable Read, 如MySQL InnoDB存儲引擎

    即使是最低的級別,也不會出現 第一類 丟失 更新問題 .

    1. 臟讀(事務沒提交,提前讀取):臟讀就是指當一個事務正在訪問數據,并且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然后使用了這個數據。

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

    Isolation 屬性一共支持五種事務設置,具體介紹如下:

    l DEFAULT 使用數據庫設置的隔離級別 ( 默認 ) ,由 DBA 默認的設置來決定隔離級別 .

    l READ_UNCOMMITTED 會出現臟讀、不可重復讀、幻讀 ( 隔離級別最低,并發性能高 )

    l READ_COMMITTED 會出現不可重復讀、幻讀問題(鎖定正在讀取的行

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

    l SERIALIZABLE 保證所有的情況不會發生(鎖表

    不可重復讀的重點是修改 :
    同樣的條件 , 你讀取過的數據 , 再次讀取出來發現值不一樣了
    幻讀的重點在于新增或者刪除
    同樣的條件 , 第 1 次和第 2 次讀出來的記錄數不一樣

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

    面臨的問題

    對于高并發高訪問的Web應用程序來說,數據庫存取瓶頸一直是個令人頭疼的問題。特別當你的程序架構還是建立在單數據庫模式,而一個數據池連接數峰值已經達到500的時候,那你的程序運行離崩潰的邊緣也不遠了。很多小網站的開發人員一開始都將注意力放在了產品需求設計上,缺忽視了程序整體性能,可擴展性等方面的考慮,結果眼看著訪問量一天天網上爬,可突然發現有一天網站因為訪問量過大而崩潰了,到時候哭都來不及。所以我們一定要未雨綢繆,在數據庫還沒罷工前,想方設法給它減負,這也是這篇文章的主要議題。

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

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

    Memcached 介紹

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

    Memcached處理的原子是每一個(key,value)對(以下簡稱kv對),key會通過一個hash算法轉化成hash-key,便于查找、對比以及做到盡可能的散列。同時,memcached用的是一個二級散列,通過一張大hash表來維護。

    Memcached有兩個核心組件組成:服務端(ms)和客戶端(mc),在一個memcached的查詢中,mc先通過計算key的hash值來確定kv對所處在的ms位置。當ms確定后,客戶端就會發送一個查詢請求給對應的ms,讓它來查找確切的數據。因為這之間沒有交互以及多播協議,所以memcached交互帶給網絡的影響是最小化的。

    舉例說明:考慮以下這個場景,有三個mc分別是X,Y,Z,還有三個ms分別是A,B,C:

    設置kv對
    X想設置key=”foo”,value=”seattle”
    X拿到ms列表,并對key做hash轉化,根據hash值確定kv對所存的ms位置
    B被選中了
    X連接上B,B收到請求,把(key=”foo”,value=”seattle”)存了起來

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

    Memcached服務器(ms)

    內存分配

    默認情況下,ms是用一個內置的叫“塊分配器”的組件來分配內存的。舍棄c++標準的malloc/free的內存分配,而采用塊分配器的主要目的是為了避免內存碎片,否則操作系統要花費更多時間來查找這些邏輯上連續的內存塊(實際上是斷開的)。用了塊分配器,ms會輪流的對內存進行大塊的分配,并不斷重用。當然由于塊的大小各不相同,當數據大小和塊大小不太相符的情況下,還是有可能導致內存的浪費。

    同時,ms對key和data都有相應的限制,key的長度不能超過250字節,data也不能超過塊大小的限制 --- 1MB。
    因為mc所使用的hash算法,并不會考慮到每個ms的內存大小。理論上mc會分配概率上等量的kv對給每個ms,這樣如果每個ms的內存都不太一樣,那可能會導致內存使用率的降低。所以一種替代的解決方案是,根據每個ms的內存大小,找出他們的最大公約數,然后在每個ms上開n個容量=最大公約數的instance,這樣就等于擁有了多個容量大小一樣的子ms,從而提供整體的內存使用率。

    緩存策略

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

    同時ms采用是偷懶替代法,ms不會開額外的進程來實時監測過時的kv對并刪除,而是當且僅當,新來一個插入的數據,而此時又沒有多余的空間放了,才會進行清除動作。

    緩存數據庫查詢
    現在memcached最流行的一種使用方式是緩存數據庫查詢,下面舉一個簡單例子說明:

    App需要得到userid=xxx的用戶信息,對應的查詢語句類似:

    “SELECT * FROM users WHERE userid = xxx”

    App先去問cache,有沒有“user:userid”(key定義可預先定義約束好)的數據,如果有,返回數據;如果沒有,App會從數據庫中讀取數據,并調用cache的add函數,把數據加入cache中。

    當取的數據需要更新,app會調用cache的update函數,來保持數據庫與cache的數據同步。

    從上面的例子我們也可以發現,一旦數據庫的數據發現變化,我們一定要及時更新cache中的數據,來保證app讀到的是同步的正確數據。當然我們可以通過定時器方式記錄下cache中數據的失效時間,時間一過就會激發事件對cache進行更新,但這之間總會有時間上的延遲,導致app可能從cache讀到臟數據,這也被稱為狗洞問題。(以后我會專門描述研究這個問題)

    數據冗余與故障預防

    從設計角度上,memcached是沒有數據冗余環節的,它本身就是一個大規模的高性能cache層,加入數據冗余所能帶來的只有設計的復雜性和提高系統的開支。

    當一個ms上丟失了數據之后,app還是可以從數據庫中取得數據。不過更謹慎的做法是在某些ms不能正常工作時,提供額外的ms來支持cache,這樣就不會因為app從cache中取不到數據而一下子給數據庫帶來過大的負載。

    同時為了減少某臺ms故障所帶來的影響,可以使用“熱備份”方案,就是用一臺新的ms來取代有問題的ms,當然新的ms還是要用原來ms的IP地址,大不了數據重新裝載一遍。

    另外一種方式,就是提高你ms的節點數,然后mc會實時偵查每個節點的狀態,如果發現某個節點長時間沒有響應,就會從mc的可用server列表里刪除,并對server節點進行重新hash定位。當然這樣也會造成的問題是,原本key存儲在B上,變成存儲在C上了。所以此方案本身也有其弱點,最好能和“熱備份”方案結合使用,就可以使故障造成的影響最小化。

    Memcached客戶端(mc)

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

    緩存式的Web應用程序架構
    有了緩存的支持,我們可以在傳統的app層和db層之間加入cache層,每個app服務器都可以綁定一個mc,每次數據的讀取都可以從ms中取得,如果沒有,再從db層讀取。而當數據要進行更新時,除了要發送update的sql給db層,同時也要將更新的數據發給mc,讓mc去更新ms中的數據。

    假設今后我們的數據庫可以和ms進行通訊了,那可以將更新的任務統一交給db層,每次數據庫更新數據的同時會自動去更新ms中的數據,這樣就可以進一步減少app層的邏輯復雜度。如下圖:

    不過每次我們如果沒有從cache讀到數據,都不得不麻煩數據庫。為了最小化數據庫的負載壓力,我們可以部署數據庫復寫,用slave數據庫來完成讀取操作,而master數據庫永遠只負責三件事:1.更新數據;2.同步slave數據庫;3.更新cache。如下圖:

    以上這些緩存式web架構在實際應用中被證明是能有效并能極大地降低數據庫的負載同時又能提高web的運行性能。當然這些架構還可以根據具體的應用環境進行變種,以達到不同硬件條件下性能的最優化。

    未來的憧憬

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

         摘要: 1. 基本 概念 IO 是主存和外部設備 ( 硬盤、終端和網絡等 ) 拷貝數據的過程。 IO 是操作系統的底層功能實現,底層通過 I/O 指令進行完成。 所有語言運行時系統提供執行 I/O 較高級別的工具。 (c 的 printf scanf,java 的面向對象封裝 ) 2. Java 標準 io 回顧 Java 標準 IO 類庫是 io 面向對象的一種抽象?;诒镜胤椒ǖ牡讓訉?..  閱讀全文

    posted @ 2011-11-08 21:25 云云 閱讀(1436) | 評論 (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.");
    }
    }

    這是一個實現多線程的程序,運行結果如下:
    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()會出現什么結果?
    修改之后運行結果:
    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.
    程序運行成為了單線程順序執行。為什么?
    start方法:用來啟動一個線程, 這時此線程是處于就緒狀態, 并沒有運行。 然后通過此Thread類調用方法run()來完成其運行操作的, 這里方法run()稱為線程體, 它包含了要執行的這個線程的內容, run方法運行結束, 此線程終止, 而CPU再運行其它線程,
    直接用run方法: 這只是調用一個方法而已, 程序中依然只有主線程--這一個線程, 其程序執行路徑還是只有一條, 這樣就沒有達到寫線程的目的。
    記?。壕€程就是為了更好地利用CPU,提高程序運行速率的!

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

    在java中可有兩種方式實現多線程,一種是繼承Thread類,一種是實現Runnable接口;Thread類是在java.lang包中定義的。一個類只要繼承了Thread類同時覆寫了本類中的run()方法就可以實現多線程操作了,但是一個類只能繼承一個父類,這是此方法的局限,下面看例子:
    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();
    }
    }
    但是,此時結果很有規律,先第一個對象執行,然后第二個對象執行,并沒有相互運行。在JDK的文檔中可以發現,一旦調用start()方法,則會通過 JVM找到run()方法。下面啟動
    start()方法啟動線程:
    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();
    }
    };

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

    Runnable接口
    在實際開發中一個多線程的操作很少使用Thread類,而是通過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類中才有。此時觀察Thread類,有一個構造方法:public Thread(Runnable targer)
    此構造方法接受Runnable的子類實例,也就是說可以通過Thread類來啟動Runnable實現的多線程。(start()可以協調系統的資源):
    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();
    }
    }
    兩種實現方式的區別和聯系:
    在程序開發中只要是多線程肯定永遠以實現Runnable接口為主,因為實現Runnable接口相比繼承Thread類有如下好處:
    ->避免點繼承的局限,一個類可以繼承多個接口。
    ->適合于資源的共享
    以賣票程序為例,通過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--);
    }
    }
    }
    };
    下面通過三個線程對象,同時賣票:
    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();//每個線程都各賣了10張,共賣了30張票
    mt2.start();//但實際只有10張票,每個線程都賣自己的票
    mt3.start();//沒有達到資源共享
    }
    }
    如果用Runnable就可以實現資源共享,下面看例子:
    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();//同一個mt,但是在Thread中就不可以,如果用同一
    new Thread(mt).start();//個實例化對象mt,就會出現異常
    new Thread(mt).start();
    }
    };
    雖然現在程序中有三個線程,但是一共賣了10張票,也就是說使用Runnable實現多線程可以達到資源共享目的。

    Runnable接口和Thread之間的聯系:
    public class Thread extends Object implements Runnable
    發現Thread類也是Runnable接口的子類。

    因為一個線程只能啟動一次,通過Thread實現線程時,線程和線程所要執行的任務是捆綁在一起的。也就使得一個任務只能啟動一個線程,不同的線程執行的任務是不相同的,所以沒有必要,也不能讓兩個線程共享彼此任務中的資源。

    一個任務可以啟動多個線程,通過Runnable方式實現的線程,實際是開辟一個線程,將任務傳遞進去,由此線程執行。可以實例化多個 Thread對象,將同一任務傳遞進去,也就是一個任務可以啟動多個線程來執行它。這些線程執行的是同一個任務,所以他們的資源是共享。

    兩種不同的線程實現方式本身就決定了其是否能進行資源共享。


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

         摘要:  Spring AOP: Spring之面向方面編程 5.1. 概念 面向方面編程 (AOP) 提供從另一個角度來考慮程序結構以完善面向對象編程(OOP)。 面向對象將應用程序分解成 各個層次的對象,而AOP將程序分解成各個方面 或者說 關注點 。 這使得可以模塊化諸如事務管理等這些橫切多個對象的關注點。(這些關注點術語稱作 橫切關注點。) Spring的一...  閱讀全文

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

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

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

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

    而 Comparator 是一個專用的比較器,當這個對象不支持自比較或者自比較函數不能滿足你的要求時,你可以寫一個比較器來完成兩個對象之間大小的比較。

      可以說一個是自己完成比較,一個是外部程序實現比較的差別而已。

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

      比如:你想對整數采用絕對值大小來排序,Integer 是不符合要求的,你不需要去修改 Integer 類(實際上你也不能這么做)去改變它的排序行為,只要使用一個實現了 Comparator 接口的對象來實現控制它的排序就行了。
    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) | 評論 (0)編輯 收藏

    僅列出標題
    共12頁: First 上一頁 4 5 6 7 8 9 10 11 12 下一頁 
    主站蜘蛛池模板: 亚洲色大情网站www| 久久成人永久免费播放| 免费A级毛片无码A| 免费人成激情视频在线观看冫| 亚洲一区二区三区四区在线观看 | 无人在线观看免费高清视频| 亚洲色偷精品一区二区三区| 亚洲欧洲自拍拍偷午夜色无码| 18国产精品白浆在线观看免费 | 亚洲视频中文字幕在线| 日本免费的一级v一片| 国产一级黄片儿免费看| 涩涩色中文综合亚洲| 亚洲熟妇无码AV在线播放 | 亚洲黄色网站视频| 亚洲?v女人的天堂在线观看| 30岁的女人韩剧免费观看| 色窝窝亚洲AV网在线观看| 亚洲最大的成网4438| 亚洲色一色噜一噜噜噜| 毛片免费全部播放一级| 你懂的免费在线观看网站| 精品一区二区三区无码免费直播| 亚洲网址在线观看你懂的| 亚洲A丁香五香天堂网| 国国内清清草原免费视频99| 波多野结衣免费一区视频 | 日本久久久久亚洲中字幕| 亚洲国产a级视频| 国产精品免费观看久久| 免费在线中文日本| 在线播放免费人成视频网站| 亚洲一本一道一区二区三区| 亚洲另类激情综合偷自拍| 中文字幕中韩乱码亚洲大片 | 久久久久久亚洲精品不卡| 大学生一级特黄的免费大片视频| 久久久久久一品道精品免费看| 亚美影视免费在线观看| 最新亚洲人成无码网www电影| 亚洲一区二区三区久久|