熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

Java多線程初學者指南(10):使用Synchronized關鍵字同步類方法

2013-11-23 19:49:57  來源: Java高級技術 

  要想解決髒數據的問題最簡單的方法就是使用synchronized關鍵字來使run方法同步代碼如下

   public synchronized void run()
{
     
}

  從上面的代碼可以看出只要在void和public之間加上synchronized關鍵字就可以使run方法同步也就是說對於同一個Java類的對象實例run方法同時只能被一個線程調用並當前的run執行完後才能被其他的線程調用即使當前線程執行到了run方法中的yield方法也只是暫停了一下由於其他線程無法執行run方法因此最終還是會由當前的線程來繼續執行先看看下面的代碼

  sychronized關鍵字只和一個對象實例綁定

     class Test
  {
        public synchronized void method()
       {
            
       }
  }
   
  public class Sync implements Runnable
  {
       private Test test;
       public void run()
       {
            thod();
       }
       public Sync(Test test)
       {
           thistest = test;
       }
       public static void main(String[] args) throws Exception
       {
           Test test =  new Test();
           Test test =  new Test();
           Sync sync = new Sync(test);
           Sync sync = new Sync(test);
           new Thread(sync)start();
           new Thread(sync)start(); 
       }
   }

  在Test類中的method方法是同步的但上面的代碼建立了兩個Test類的實例因此test和test的method方法是分別執行的要想讓method同步必須在建立Sync類的實例時向它的構造方法中傳入同一個Test類的實例如下面的代碼所示

   Sync sync = new Sync(test);

  不僅可以使用synchronized來同步非靜態方法也可以使用synchronized來同步靜態方法如可以按如下方式來定義method方法

   class Test 
{
    public static synchronized void method() {   }
}

  建立Test類的對象實例如下

   Test test = new Test();

  對於靜態方法來說只要加上了synchronized關鍵字這個方法就是同步的無論是使用thod()還是使用thod()來調用method方法method都是同步的並不存在非靜態方法的多個實例的問題

  在種設計模式中的單件(Singleton)模式如果按傳統的方法設計也是線程不安全的下面的代碼是一個線程不安全的單件模式

   package test;

// 線程安全的Singleton模式
class Singleton
{
    private static Singleton sample;

    private Singleton()
    {
    }
    public static Singleton getInstance()
    {
        if (sample == null)
        {
            Threadyield(); // 為了放大Singleton模式的線程不安全性
            sample = new Singleton();
        }
        return sample;
    }
}
public class MyThread extends Thread
{
    public void run()
    {
        Singleton singleton = SingletongetInstance();
        Systemoutprintln(singletonhashCode());
    }
    public static void main(String[] args)
    {
        Thread threads[] = new Thread[];
        for (int i = ; i < threadslength; i++)
            threads[i] = new MyThread();
        for (int i = ; i < threadslength; i++)
            threads[i]start();
    }
}

  在上面的代碼調用yield方法是為了使單件模式的線程不安全性表現出來如果將這行去掉上面的實現仍然是線程不安全的只是出現的可能性小得多

  程序的運行結果如下

  



  上面的運行結果可能在不同的運行環境上有所有同但一般這五行輸出不會完全相同從這個輸出結果可以看出通過getInstance方法得到的對象實例是五個而不是我們期望的一個這是因為當一個線程執行了Threadyield()後就將CPU資源交給了另外一個線程由於在線程之間切換時並未執行到創建Singleton對象實例的語句因此這幾個線程都通過了if判斷所以就會產生了建立五個對象實例的情況(可能創建的是四個或三個對象實例這取決於有多少個線程在創建Singleton對象之前通過了if判斷每次運行時可能結果會不一樣)

  要想使上面的單件模式變成線程安全的只要為getInstance加上synchronized關鍵字即可代碼如下

   public static synchronized Singleton getInstance() {   }

  當然還有更簡單的方法就是在定義Singleton變量時就建立Singleton對象代碼如下

   private static final Singleton sample = new Singleton();

  然後在getInstance方法中直接將sample返回即可這種方式雖然簡單但不知在getInstance方法中創建Singleton對象靈活讀者可以根據具體的需求選擇使用不同的方法來實現單件模式

  在使用synchronized關鍵字時有以下四點需要注意

    synchronized關鍵字不能繼承

  雖然可以使用synchronized來定義方法但synchronized並不屬於方法定義的一部分因此synchronized關鍵字不能被繼承如果在父類中的某個方法使用了synchronized關鍵字而在子類中覆蓋了這個方法在子類中的這個方法默認情況下並不是同步的而必須顯式地在子類的這個方法中加上synchronized關鍵字才可以當然還可以在子類方法中調用父類中相應的方法這樣雖然子類中的方法不是同步的但子類調用了父類的同步方法因此子類的方法也就相當於同步了這兩種方式的例子代碼如下

  在子類方法中加上synchronized關鍵字

   class Parent
{
    public synchronized void method() {   }
}
class Child extends Parent
{
    public synchronized void method() {   }
}

  在子類方法中調用父類的同步方法

   class Parent
{
    public synchronized void method() {   }
}
class Child extends Parent
{
    public void method() { thod();   }
}

    在定義接口方法時不能使用synchronized關鍵字

    構造方法不能使用synchronized關鍵字但可以使用下節要討論的synchronized塊來進行同步

    synchronized可以自由放置

  在前面的例子中使用都是將synchronized關鍵字放在方法的返回類型前面但這並不是synchronized可放置唯一位置在非靜態方法中synchronized還可以放在方法定義的最前面在靜態方法中synchronized可以放在static的前面代碼如下

   public synchronized void method();
synchronized public void method();
public static synchronized void method();
public synchronized static void method();
synchronized public static void method();

  但要注意synchronized不能放在方法返回類型的後面如下面的代碼是錯誤的

   public void synchronized method();
public static void synchronized method();

  synchronized關鍵字只能用來同步方法不能用來同步類變量如下面的代碼也是錯誤的

   public synchronized int n = ;
public static synchronized int n = ;

  雖然使用synchronized關鍵字同步方法是最安全的同步方式但大量使用synchronized關鍵字會造成不必要的資源消耗以及性能損失雖然從表面上看synchronized鎖定的是一個方法但實際上synchronized鎖定的是一個類也就是說如果在非靜態方法method和method定義時都使用了synchronized在method未執行完之前method是不能執行的靜態方法和非靜態方法的情況類似但靜態和非靜態方法不會互相影響看看如下的代碼

   package test;

public class MyThread extends Thread
{
    public String methodName;

    public static void method(String s)
    {
        Systemoutprintln(s);
        while (true)
            ;
    }
    public synchronized void method()
    {
        method(非靜態的method方法);
    }
    public synchronized void method()
    {
        method(非靜態的method方法);
    }
    public static synchronized void method()
    {
        method(靜態的method方法);
    }
    public static synchronized void method()
    {
        method(靜態的method方法);
    }
    public void run()
    {
        try
        {
            getClass()getMethod(methodName)invoke(this);
        }
        catch (Exception e)
        {
        }
    }
    public static void main(String[] args) throws Exception
    {
        MyThread myThread = new MyThread();
        for (int i = ; i <= ; i++)
        {
            thodName = method + StringvalueOf(i);
            new Thread(myThread)start();
            sleep();
        }
    }
}

  運行結果如下

   非靜態的method方法
靜態的method方法

  從上面的運行結果可以看出method和method在method和method未結束之前不能運行因此我們可以得出一個結論如果在類中使用synchronized關鍵字來定義非靜態方法那將影響這個中的所有使用synchronized關鍵字定義的非靜態方法如果定義的是靜態方法那麼將影響類中所有使用synchronized關鍵字定義的靜態方法這有點象數據表中的表鎖當修改一條記錄時系統就將整個表都鎖住了因此大量使用這種同步方式會使程序的性能大幅度下降


From:http://tw.wingwit.com/Article/program/Java/gj/201311/27526.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.