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

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

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

    Picses' sky

    Picses' sky
    posts - 43, comments - 29, trackbacks - 0, articles - 24

    用Java線程獲取優異性能(II)3

    Posted on 2007-07-18 13:13 Matthew Chen 閱讀(212) 評論(0)  編輯  收藏 所屬分類: Java MultiThread
    同步機制的兩個問題
    盡管其簡單,開發者經常濫用Java的同步機制會導致程序由不同步變得死鎖。這章將檢查這些問題并提供一對避免它們的建議。
    注意:一個與同步機制有關的線程問題是與鎖的獲得和釋放有關的時間成本。換句話說,一個線程將花費時間去獲得或釋放一個鎖。當在一個循環中獲得/釋放一個鎖,單獨的時間成本合計起來就會降低性能。對于舊的JVMs,鎖的獲得時間成本經常導致重大的性能損失。幸運地是, Sun微系統的HotSpot JVM (其裝載在J2SE SDK上)提供快速的鎖的獲得和釋放,大大減少了對這些程序的影響。
    不同步
    在一個線程自動或不自動(通過一個例外)退出一個關鍵代碼部份時,它釋放一個鎖以便另一個線程能夠得以進入。假設兩個線程想進入同一個關鍵代碼部份,為了阻止兩個線程同時進入那個關鍵代碼部份,每個線程必須努力獲得同一個鎖。如果每一個線程企圖獲得一個不同的鎖并成功了,兩個線程都進入了關鍵代碼部份,則兩個線程都不得不等待其它線程釋放它的鎖因為其它線程獲得了一個不同的鎖。最終結果是:沒有同步。示范如列表4:
    列表4. NoSynchronizationDemo.java
    // NoSynchronizationDemo.java
    class NoSynchronizationDemo
    {
    public static void main (String [] args)
    {
    FinTrans ft = new FinTrans ();
    TransThread tt1 = new TransThread (ft, "Deposit Thread");
    TransThread tt2 = new TransThread (ft, "Withdrawal Thread");
    tt1.start ();
    tt2.start ();
    }
    }
    class FinTrans
    {
    public static String transName;
    public static double amount;
    }
    class TransThread extends Thread
    {
    private FinTrans ft;
    TransThread (FinTrans ft, String name)
    {
    super (name); //保存線程的名稱
    this.ft = ft; //保存對金融事務對象的引用
    }
    public void run ()
    {
    for (int i = 0; i < 100; i++)
    {
    if (getName ().equals ("Deposit Thread"))
    {
    synchronized (this)
    {
    ft.transName = "Deposit";
    try
    {
    Thread.sleep ((int) (Math.random () * 1000));
    }
    catch (InterruptedException e)
    {
    }
    ft.amount = 2000.0;
    System.out.println (ft.transName + " " + ft.amount);
    }
    }
    else
    {
    synchronized (this)
    {
    ft.transName = "Withdrawal";
    try
    {
    Thread.sleep ((int) (Math.random () * 1000));
    }
    catch (InterruptedException e)
    {
    }
    ft.amount = 250.0;
    System.out.println (ft.transName + " " + ft.amount);
    }
    }
    }
    }
    }
    當你運行NoSynchronizationDemo時,你將看到類似如下的輸出:
    Withdrawal 250.0
    Withdrawal 2000.0
    Deposit 250.0
    Withdrawal 2000.0
    Deposit 2000.0
    盡管使用了synchronized聲明,但沒有同步發生。為什么?檢查synchronized (this)。因為關鍵字this指向當前對象,存款線程企圖獲得與初始化分配給tt1的TransThread對象引用有關的鎖。 (在main()方法中)。類似的,取款線程企圖獲得與初始化分配給tt2的TransThread對象引用有關的鎖。我們有兩個不同的TransThread對象,并且每一個線程企圖在進入它自己關鍵代碼部份前獲得與其各自TransThread對象相關的鎖。因為線程獲得不同的鎖,兩個線程都能在同一時間進入它們自己的關鍵代碼部份。結果是沒有同步。
    技巧:為了避免一個沒有同步的情形,選擇一個對于所有相關線程都公有的對象。那樣的話,這些線程競相獲得同一個對象的鎖,并且同一時間僅有一個線程在能夠進入相關的關鍵代碼部份。
    死鎖
    在有些程序中,下面的情形可能出現:在線程B能夠進入B的關鍵代碼部份前線程A獲得一個線程B需要的鎖。類似的,在線程A能夠進入A的關鍵代碼部份前線程B獲得一個線程A需要的鎖。因為兩個線程都沒有擁有它自己需要的鎖,每個線程都必須等待獲得它的鎖。此外,因為沒有線程能夠執行,沒有線程能夠釋放其它線程的鎖,并且程序執行被凍結。這種行為叫作死鎖(deadlock)。其示范列如表5:
    列表5. DeadlockDemo.java
    // DeadlockDemo.java
    class DeadlockDemo
    {
    public static void main (String [] args)
    {
    FinTrans ft = new FinTrans ();
    TransThread tt1 = new TransThread (ft, "Deposit Thread");
    TransThread tt2 = new TransThread (ft, "Withdrawal Thread");
    tt1.start ();
    tt2.start ();
    }
    }
    class FinTrans
    {
    public static String transName;
    public static double amount;
    }
    class TransThread extends Thread
    {
    private FinTrans ft;
    private static String anotherSharedLock = "";
    TransThread (FinTrans ft, String name)
    {
    super (name); //保存線程的名稱
    this.ft = ft; //保存對金融事務對象的引用
    }
    public void run ()
    {
    for (int i = 0; i < 100; i++)
    {
    if (getName ().equals ("Deposit Thread"))
    {
    synchronized (ft)
    {
    synchronized (anotherSharedLock)
    {
    ft.transName = "Deposit";
    try
    {
    Thread.sleep ((int) (Math.random () * 1000));
    }
    catch (InterruptedException e)
    {
    }
    ft.amount = 2000.0;
    System.out.println (ft.transName + " " + ft.amount);
    }
    }
    }
    else
    {
    synchronized (anotherSharedLock)
    {
    synchronized (ft)
    {
    ft.transName = "Withdrawal";
    try
    {
    Thread.sleep ((int) (Math.random () * 1000));
    }
    catch (InterruptedException e)
    {
    }
    ft.amount = 250.0;
    System.out.println (ft.transName + " " + ft.amount);
    }
    }
    }
    }
    }
    }
    如果你運行DeadlockDemo,你將可能看到在應用程序凍結前僅一個單獨輸出行。要解凍DeadlockDemo,按Ctrl-C (假如你正在一個Windows命令提示符中使用Sun的SDK1.4)。
    什么將引起死鎖呢?仔細查看源代碼。存款線程必須在它能夠進入其內部關鍵代碼部份前獲得兩個鎖。與ft引用的FinTrans對象有關的外部鎖和與anotherSharedLock引用的String對象有關的內部鎖。類似的,取款線程必須在其能夠進入它自己的內部關鍵代碼部份前獲得兩個鎖。與anotherSharedLock引用的String對象有關的外部鎖和與ft引用的FinTrans對象有關的內部鎖。假定兩個線程的執行命令是每個線程獲得它的外部鎖。因此,存款線程獲得它的FinTrans鎖,以及取款線程獲得它的String鎖。現在兩個線程都執行它們的外部鎖,它們處在它們相應的外部關鍵代碼部份。兩個線程接下來企圖獲得內部鎖,因此它們能夠進入相應的內部關鍵代碼部份。
    存款線程企圖獲得與anotherSharedLock引用對象相關的鎖。然而,因為取款線程控制著鎖所以存款線程必須等待。類似的,取款線程企圖獲得與ft引用對象相關的鎖。但是取款線程不能獲得那個鎖因為存款線程(它正在等待)控制著它。因此,取款線程也必須等待。兩個線程都不能操作因為兩個線程都不能釋放它控制著的鎖。兩個線程不能釋放它控制著的鎖是因為每個線程都正在等待。每個線程都死鎖,并且程序凍結。
    技巧:為了避免死鎖,仔細分析你的源代碼看看當一個同步方法調用其它同步方法時什么地方可能出現線程互相企圖獲得彼此的鎖。你必須這樣做因為JVM不能探測并防止死鎖。
    回顧
    為了使用線程達到優異性能,你將遇到你的多線程程序需要連載訪問關鍵代碼部份的情形。同步可以有效地阻止在奇怪程序行為中產生的不一致。你能夠使用synchronized聲明以保護一個方法的部份,或同步整個方法。但應仔細檢查你的代碼以防止可能造成同步失敗或死鎖的故障。
    主站蜘蛛池模板: 97青青草原国产免费观看| 久久久亚洲欧洲日产国码aⅴ| 日本在线看片免费人成视频1000| 国产成人精品亚洲| 亚洲制服丝袜第一页| 亚洲国产成人久久精品动漫| 亚洲阿v天堂在线2017免费| 成人毛片18女人毛片免费96| 99re6热视频精品免费观看| 国产成人精品免费视频大全| 亚洲AV日韩AV无码污污网站| 亚洲宅男天堂a在线| 亚洲免费视频在线观看| 亚洲精品二区国产综合野狼| 免费国产怡红院在线观看| 在线观看人成网站深夜免费| 无码乱肉视频免费大全合集| 67194国产精品免费观看| AAA日本高清在线播放免费观看| jizz中国免费| 一级毛片免费播放男男| 精品久久久久久亚洲综合网| 亚洲人成77777在线观看网| 亚洲免费中文字幕| 亚洲黄色在线视频| 亚洲黄色网址在线观看| 亚洲精品午夜视频| 亚洲精品第五页中文字幕| 亚洲欧洲国产成人精品| 亚洲视频在线观看网站| 久久亚洲国产精品成人AV秋霞| 日韩亚洲Av人人夜夜澡人人爽| 亚洲成人在线电影| 亚洲精品午夜久久久伊人| 亚洲一区二区三区精品视频| 亚洲欧洲日本在线观看 | 中文字幕乱理片免费完整的| 一级毛片免费播放视频| 中国国语毛片免费观看视频| 国产久爱免费精品视频| a级大片免费观看|