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

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

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

    隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
    數據加載中……

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

    本文為原創,如需轉載,請注明作者和出處,謝謝!

    上一篇:Java多線程初學者指南(9):為什么要進行數據同步

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

    public synchronized void run()
    {
         
    }

    從上面的代碼可以看出,只要在voidpublic之間加上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()
           {
                test.method();
           }
          
    public Sync(Test test)
           {
              
    this.test = test;
           }
          
    public static void main(String[] args) throws Exception
           {
               Test test1 
    =  new Test();
               Test test2 
    =  new Test();
               Sync sync1 
    = new Sync(test1);
               Sync sync2 
    = new Sync(test2);
              
    new Thread(sync1).start();
              
    new Thread(sync2).start(); 
           }
       }

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

    Sync sync1 = new Sync(test1);
        不僅可以使用synchronized來同步非靜態方法,也可以使用synchronized來同步靜態方法。如可以按如下方式來定義method方法:
    class Test 
    {
        public static synchronized void method() {   }
    }

    建立Test類的對象實例如下:

    Test test = new Test();

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

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

    package test;

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

        
    private Singleton()
        {
        }
        
    public static Singleton getInstance()
        {
            
    if (sample == null)
            {
                Thread.yield(); 
    // 為了放大Singleton模式的線程不安全性
                sample = new Singleton();
            }
            
    return sample;
        }
    }
    public class MyThread extends Thread
    {
        
    public void run()
        {
            Singleton singleton 
    = Singleton.getInstance();
            System.out.println(singleton.hashCode());
        }
        
    public static void main(String[] args)
        {
            Thread threads[] 
    = new Thread[5];
            
    for (int i = 0; i < threads.length; i++)
                threads[i] 
    = new MyThread();
            
    for (int i = 0; i < threads.length; i++)
                threads[i].start();
        }
    }

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

    程序的運行結果如下:

    25358555
    26399554
    7051261
    29855319
    5383406

    上面的運行結果可能在不同的運行環境上有所有同,但一般這五行輸出不會完全相同。從這個輸出結果可以看出,通過getInstance方法得到的對象實例是五個,而不是我們期望的一個。這是因為當一個線程執行了Thread.yield()后,就將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關鍵字時有以下四點需要注意:

    1.  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() { super.method();   }
    }

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

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

    4.  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 = 0;
    public static synchronized int n = 0;

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

    package test;

    public class MyThread1 extends Thread
    {
        
    public String methodName;

        
    public static void method(String s)
        {
            System.out.println(s);
            
    while (true)
                ;
        }
        
    public synchronized void method1()
        {
            method(
    "非靜態的method1方法");
        }
        
    public synchronized void method2()
        {
            method(
    "非靜態的method2方法");
        }
        
    public static synchronized void method3()
        {
            method(
    "靜態的method3方法");
        }
        
    public static synchronized void method4()
        {
            method(
    "靜態的method4方法");
        }
        
    public void run()
        {
            
    try
            {
                getClass().getMethod(methodName).invoke(
    this);
            }
            
    catch (Exception e)
            {
            }
        }
        
    public static void main(String[] args) throws Exception
        {
            MyThread1 myThread1 
    = new MyThread1();
            
    for (int i = 1; i <= 4; i++)
            {
                myThread1.methodName 
    = "method" + String.valueOf(i);
                
    new Thread(myThread1).start();
                sleep(
    100);
            }
        }
    }

    運行結果如下:

    非靜態的method1方法
    靜態的method3方法

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

    下一篇:Java多線程初學者指南(11):使用Synchronized塊同步方法





    Android開發完全講義(第2版)(本書版權已輸出到臺灣)

    http://product.dangdang.com/product.aspx?product_id=22741502



    Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


    新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

    posted on 2009-03-20 13:05 銀河使者 閱讀(9748) 評論(4)  編輯  收藏 所屬分類: java 原創多線程

    評論

    # re: Java多線程初學者指南(10):使用Synchronized關鍵字同步類方法  回復  更多評論   

    我想問一下:

    MyThread myThread = new MyThread();
    new Thread(myThread).start();

    這里可不可以直接寫成:
    MyThread myThread = new MyThread();
    myThread.start();

    我試了一下,這種方法可以直接運行,請問這兩種方法有什么不同?
    2009-03-20 16:23 | lveyo

    # re: Java多線程初學者指南(10):使用Synchronized關鍵字同步類方法  回復  更多評論   

    @lveyo
    這兩種方法沒什么太大的區別,如果用第一種myThread相當于一個回調對象,調用start方法時調用了myThread對象中的run方法。但有一個區別,如果用第一種方法,MyThread類可以實現Runnable接口,也可以繼承Thread類,而使用第二種方法,MyThread只能是Thread類的只類,如果只實現Runnable接口的類是不能用第二種方法的。
    2009-03-20 16:39 | 銀河使者

    # re: Java多線程初學者指南(10):使用Synchronized關鍵字同步類方法  回復  更多評論   

    @銀河使者
    明白了,謝謝,一直看你關于線程的介紹呢,明白了很多,概念清晰了
    2009-03-20 20:14 | lveyo

    # re: Java多線程初學者指南(10):使用Synchronized關鍵字同步類方法  回復  更多評論   

    >>要想解決“臟數據”的問題,最簡單的方法就是使用synchronized關鍵字來使run方法同步,
    這個方法是不行的,synchronized =synchronized(this)只對當前對象有效,所以還是會產生臟數據。
    2014-04-30 15:21 | wsh
    主站蜘蛛池模板: 最近免费视频中文字幕大全| 特级无码毛片免费视频尤物| 免费无码AV电影在线观看| 亚洲人成在线影院| 免费A级毛片无码A∨中文字幕下载| 在线观看亚洲精品福利片| 国产精品免费视频观看拍拍| 久久久久国产成人精品亚洲午夜| 四虎成人精品国产永久免费无码| 国产又黄又爽又猛的免费视频播放| 国产精品成人亚洲| 亚洲国产精品无码久久九九| 黄色视频在线免费观看| 日韩亚洲一区二区三区| 2021精品国产品免费观看| 亚洲AV无码一区二区三区人| 日韩午夜免费视频| japanese色国产在线看免费| 久久久久亚洲精品影视| 久久免费看黄a级毛片| 亚洲人成无码网站在线观看| 亚洲?V无码成人精品区日韩| 免费人成在线观看视频高潮| 亚洲国产精品综合福利专区| 夭天干天天做天天免费看| 一级美国片免费看| 亚洲成人动漫在线| 免费羞羞视频网站| 中文字幕免费在线播放| 成人性生交大片免费看中文| 综合自拍亚洲综合图不卡区| 卡一卡二卡三在线入口免费| WWW国产成人免费观看视频| 亚洲视频在线观看网址| 国产免费看插插插视频| 国产无遮挡无码视频免费软件| 久久国产亚洲精品| 亚洲精品乱码久久久久久中文字幕| 青娱分类视频精品免费2| 人妻无码中文字幕免费视频蜜桃| 久久久久亚洲av无码尤物|