Static與線程安全

轉自:http://yinlei555.javaeye.com/blog/707986
作者:yinlei555

先來說說Static的用法之後介紹線程安全的相關知識。
類(static)變量    在所有類的實例中共享,可以被標記爲public或private
    如果被標記爲public而沒有該類的實例,可以從該類的外部訪問。
    有時想有一個可以在類的所有實例中共享的變量。比如,這可以用作實例之間交流的基礎或追蹤已經創建的實例的數量。
    可以用關鍵字static來標記變量的辦法獲得這個效果。這樣的變量有時被叫做class variable,以便與不共享的成員或實例變量區分開來。
    Static變量在某種程度上與其它語言中的全局變量相似。Java編程語言沒有這樣的全局語言,但static變量是可以從類的任何實例訪問的單個變量。
    如果static變量沒有被標記成private,它可能會被從該類的外部進行訪問。要這樣做,不需要類的實例,可以通過類名指向它。
類(static)方法
    因爲static方法不需它所屬的類的任何實例就會被調用,因此沒有this值。結果是,static方法不能訪問與它本身的參數以及static變量分離的任何變量。訪問非靜態變量的嘗試會引起編譯錯誤。
也就是說static 的方法只能訪問static的類。
public class Wrong {
  int x;
  public static void main(String args[]) {
    x = 9; // COMPILER ERROR!
  }
}
below is right
public class Wrong {
  static int x;
  public static void main(String args[]) {
    x = 9; // COMPILER ERROR!
  }
}

 注意:
    Main()是靜態的,因爲它必須在任何實例化發生前被順序地訪問,以便應用程序的運行。
    靜態方法不能被覆蓋成非靜態。

靜態初始化程序
    方法程序體中不存在的代碼在static block中類可以包含該代碼,這是完全有效的。當類被裝載時,靜態塊代碼只執行一次。類中不同的靜態塊按它們在類中出現的順序被執行。
public class StaticInitDemo {
static int i = 5;
  static {
    System.out.println("Static code i= "+ i++ );
  }
}

public class Test {
  public static void main(String args[]) {
    System.out.println("Main code: i="
      + StaticInitDemo.i);
  }
}
Static code: i=5
Main code: i=6

 注意:
 1.Static方法和數據的單個(共享)副本是因爲類和該類的所有實例而存在。通過一個實例或通過類本身可以訪問static成員。
 2.非靜態數據只限於實例,只能通過該實例的非靜態方法對它進行訪問。非靜態數據定義對象之間互不相同的特點,非靜態方法在它們所作用的非靜態數據的基礎上對每個對象的行爲互不相同。

舉一個簡單例子說明使用:
一款鞋子的尺碼是固定的,但這個型號的顏色可以有不同的,那麼定義的時候,我們可以吧這款鞋子尺碼定義成static的,顏色值定義成非static的,。顏色根據對象的不同而不同,其行爲也根據對象的不同而不同,在它所作用的非靜態數據的基礎上對不同對象返回不同的顏色。

線程安全:
對於線程安全,我們先看兩個例子:
class CheckoutLane
{
    public static float GetTotal(Cart cart)
    {
        float total = 0;

        for (int i = 0; i < cart.GroceryItems.Length; i++)
        {
            total += cart.GroceryItems[i].Price; 
            Thread.Sleep(100);
        }
        return total;
    }

}
對於上面的代碼,你使用多少的線程來控制,都是安全的。因爲線程之間,不會共享什麼資源,唯一相同的是使用了同一個邏輯。其實這是在破壞線程的前題方面下功夫,出現線程不安全的條件都沒有了,那肯定就是線程安全的。
class CheckoutLane
{
    static float total;
    public static float GetTotal(Cart cart)
    {
        total = 0;
        for (int i = 0; i < cart.GroceryItems.Length; i++)
        {
            total += cart.GroceryItems[i].Price;  
            Thread.Sleep(100);
        }
        return total;
    }
}

對於上面的這個例子,不是線程安全的,因爲共享了static float total;這個資源,而各個線程都隨機都被調用,可以任意修改total這個數據。這個,就正如多個收銀員共享櫃檯,任意執行收銀操作一樣。

class CheckoutLane
{
    static float total;
    static object synchLock = new object();
    public static float GetTotal(Cart cart)
    {
        lock(synchLock)
        {
            total = 0;
            for (int i = 0; i < cart.GroceryItems.Length; i++)
            {
                total += cart.GroceryItems[i].Price;  
                Thread.Sleep(100);
            }
            return total;
        }
    }
}

上面的代碼是線程安全的,使用了lock關鍵字,可以達到一個線程強佔資源的效果。這時,synchLock就作爲了共享的資源,被某一個線程鎖住了。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章