eroself

          關(guān)于人生的程式,在這里譜寫......
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 ::  :: 管理

          synchronized的作用

          Posted on 2008-01-11 15:09 鬼谷子 閱讀(204) 評論(0)  編輯  收藏 所屬分類: Java
          轉(zhuǎn)自:http://www.aygfsteel.com/swingboat/archive/2007/05/08/115882.html

          synchronized的作用 
          一、同步方法
          public synchronized void methodAAA(){

          //….

          }
          鎖定的是調(diào)用這個同步方法的對象

          測試:
          a、不使用這個關(guān)鍵字修飾方法,兩個線程調(diào)用同一個對象的這個方法。
          目標(biāo)類:

          1public class TestThread {
          2    public  void execute(){  //synchronized,未修飾
          3        for(int i=0;i<100;i++){
          4            System.out.println(i);
          5        }
              
          6    }

          7}


          線程類:

          1public class ThreadA implements Runnable{
          2    TestThread test=null;
          3    public ThreadA(TestThread pTest){  //對象有外部引入,這樣保證是同一個對象
          4        test=pTest;
          5    }

          6    public void run() {
          7        test.execute();
          8    }

          9}

          調(diào)用:
          1TestThread test=new TestThread();
          2Runnable runabble=new ThreadA(test);
          3Thread a=new Thread(runabble,"A");                
          4a.start();
          5Thread b=new Thread(runabble,"B");
          6b.start();


          結(jié)果:
          輸出的數(shù)字交錯在一起。說明不是同步的,兩個方法在不同的線程中是異步調(diào)用的。

          b、修改目標(biāo)類,增加synchronized修飾

          1public class TestThread {
          2    public synchronized  void execute(){  //synchronized修飾
          3        for(int i=0;i<100;i++){
          4            System.out.println(i);
          5        }
              
          6    }

          7}


          結(jié)果:
          輸出的數(shù)字是有序的,首先輸出A的數(shù)字,然后是B,說明是同步的,雖然是不同的線程,但兩個方法是同步調(diào)用的。
          注意:上面雖然是兩個不同的線程,但是是同一個實(shí)例對象。下面使用不同的實(shí)例對象進(jìn)行測試。

          c、每個線程都有獨(dú)立的TestThread對象。
          目標(biāo)類:

          1public class TestThread {
          2    public synchronized void execute(){  //synchronized修飾
          3        for(int i=0;i<100;i++){
          4            System.out.println(i);
          5        }
              
          6    }

          7}

          線程類:
          1public class ThreadA implements Runnable{
          2    public void run() {
          3        TestThread test=new TestThread();
          4        test.execute();
          5    }

          6}

          7

          調(diào)用:
          1Runnable runabble=new ThreadA();
          2Thread a=new Thread(runabble,"A");                
          3a.start();
          4Thread b=new Thread(runabble,"B");
          5b.start();


          結(jié)果:
          輸出的數(shù)字交錯在一起。說明雖然增加了synchronized 關(guān)鍵字來修飾方法,但是不同的線程調(diào)用各自的對象實(shí)例,兩個方法仍然是異步的。

          引申:
          對于這種多個實(shí)例,要想實(shí)現(xiàn)同步即輸出的數(shù)字是有序并且按線程先后順序輸出,我們可以增加一個靜態(tài)變量,對它進(jìn)行加鎖(后面將說明鎖定的對象)。

          修改目標(biāo)類:

           1public class TestThread {
           2    private static Object lock=new Object(); //必須是靜態(tài)的。
           3    public  void execute(){
           4        synchronized(lock){
           5            for(int i=0;i<100;i++){
           6                System.out.println(i);
           7            }
              
           8        }

           9    }

          10}

          二、同步代碼塊

          1public void method(SomeObject so){
          2    synchronized(so)
          3       //…..
          4    }

          5}

          鎖定一個對象,其實(shí)鎖定的是該對象的引用(object reference)
          誰拿到這個鎖誰就可以運(yùn)行它所控制的那段代碼。當(dāng)有一個明確的對象作為鎖時,就可以按上面的代碼寫程序,但當(dāng)沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創(chuàng)建一個特殊的instance變量(它必須是一個對象)來充當(dāng)鎖(上面的解決方法就是增加了一個狀態(tài)鎖)。

          a、鎖定一個對象,它不是靜態(tài)的
          private byte[] lock = new byte[0]; // 特殊的instance變量
          目標(biāo)類:

           1public class TestThread {
           2    private Object lock=new Object(); 
           3    public  void execute(){
           4        synchronized(lock){  //增加了個鎖,鎖定了對象lock,在同一個類實(shí)例中,是線程安全的,但不同的實(shí)例還是不安全的。
           5
           6因?yàn)椴煌膶?shí)例有不同對象鎖lock
           7            for(int i=0;i<100;i++){
           8                System.out.println(i);
           9            }
              
          10        }

          11    }

          12}
            


          其實(shí)上面鎖定一個方法,等同于下面的:

          1public void execute(){  
          2    synchronized(this){   //同步的是當(dāng)然對象
          3        for(int i=0;i<100;i++){
          4            System.out.println(i);
          5        }
              
          6    }

          7}

          b、鎖定一個對象或方法,它是靜態(tài)的
          這樣鎖定,它鎖定的是對象所屬的類
          public synchronized  static void execute(){
              //...
          }
          等同于

          1public class TestThread {
          2    public static void execute(){
          3        synchronized(TestThread.class){
          4            //
          5        }

          6    }

          7}

          測試:

          目標(biāo)類:

           1public class TestThread {
           2    private static Object lock=new Object();
           3    public synchronized static void execute(){  //同步靜態(tài)方法
           4        for(int i=0;i<100;i++){
           5            System.out.println(i);
           6        }
              
           7    }

           8    public static void execute1(){
           9        for(int i=0;i<100;i++){
          10            System.out.println(i);
          11        }
              
          12    }

          13    public void test(){
          14        execute();     //輸出是有序的,說明是同步的
          15        //execute1();  //輸出是無須的,說明是異步的
          16    }

          17}

          線程類:調(diào)用不同的方法,于是建立了兩個線程類

           1public class ThreadA implements Runnable{
           2    public void run() {
           3        TestThread.execute();//調(diào)用同步靜態(tài)方法
           4    }

           5}

           6public class ThreadB implements Runnable{
           7    public void run() {
           8        TestThread test=new TestThread();
           9        test.test();//調(diào)用非同步非靜態(tài)方法
          10    }

          11}

          調(diào)用:

          1Runnable runabbleA=new ThreadA();
          2Thread a=new Thread(runabbleA,"A");                
          3a.start();
          4Runnable runabbleB=new ThreadB();
          5Thread b=new Thread(runabbleB,"B");                
          6b.start();

          注意:
          1、用synchronized 來鎖定一個對象的時候,如果這個對象在鎖定代碼段中被修改了,則這個鎖也就消失了。看下面的實(shí)例:

          目標(biāo)類:

           1public class TestThread {
           2     private static final class TestThreadHolder {
           3            private static TestThread theSingleton = new TestThread();
           4            public static TestThread getSingleton() {
           5                return theSingleton;
           6            }

           7            private TestThreadHolder() {
           8            }

           9        }

          10     
          11    private Vector ve =null;
          12    private Object lock=new Object();
          13    private TestThread(){
          14        ve=new Vector();
          15        initialize();
          16    }

          17    public static TestThread getInstance(){
          18        return TestThreadHolder.getSingleton();
          19    }

          20    private void initialize(){
          21        for(int i=0;i<100;i++){
          22            ve.add(String.valueOf(i));
          23        }

          24    }

          25    public void reload(){
          26        synchronized(lock){
          27            ve=null;            
          28            ve=new Vector();
          29                        //lock="abc"; 
          30            for(int i=0;i<100;i++){
          31                ve.add(String.valueOf(i));
          32            }

          33        }

          34        System.out.println("reload end");
          35    }

          36    
          37    public boolean checkValid(String str){
          38        synchronized(lock){
          39            System.out.println(ve.size());
          40            return ve.contains(str);
          41        }

          42    }

          43}

          說明:在reload和checkValid方法中都增加了synchronized關(guān)鍵字,對lock對象進(jìn)行加鎖。在不同線程中對同一個對象實(shí)例分別調(diào)用reload和checkValid方法。
          在reload方法中,不修改lock對象即注釋lock="abc"; ,結(jié)果在控制臺輸出reload end后才輸出100。說明是同步調(diào)用的。
          如果在reload方法中修改lock對象即去掉注釋,結(jié)果首先輸出了一個數(shù)字(當(dāng)前ve的大小),然后輸出reload end。說明是異步調(diào)用的。

          2、單例模式中對多線程的考慮

           1public class TestThread {
           2     private static final class TestThreadHolder {
           3            private static TestThread theSingleton = new TestThread();
           4            public static TestThread getSingleton() {
           5                return theSingleton;
           6            }

           7            private TestThreadHolder() {
           8            }

           9        }

          10    private Vector ve =null;
          11    private Object lock=new Object();
          12    private TestThread(){
          13        ve=new Vector();
          14        initialize();
          15    }

          16    public static TestThread getInstance(){
          17        return TestThreadHolder.getSingleton();
          18    }

          19        '''
          20}

          說明:增加了一個內(nèi)部類,在內(nèi)部類中申明一個靜態(tài)的對象,實(shí)例化該單例類,初始化的數(shù)據(jù)都在單例類的構(gòu)造函數(shù)中進(jìn)行。這樣保證了多個實(shí)例同時訪問的時候,初始化的數(shù)據(jù)都已經(jīng)成功初始化了。

          總結(jié):
          A. 無論synchronized關(guān)鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數(shù)當(dāng)作鎖,所以首先應(yīng)知道需要加鎖的對象
          B.每個對象只有一個鎖(lock)與之相關(guān)聯(lián)。
          C.實(shí)現(xiàn)同步是要很大的系統(tǒng)開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。

          主站蜘蛛池模板: 德庆县| 巧家县| 延寿县| 仁布县| 工布江达县| 龙游县| 微山县| 武平县| 邹平县| 嘉义市| 广宗县| 高安市| 蒙阴县| 莲花县| 鄢陵县| 罗山县| 二连浩特市| 贞丰县| 黑水县| 安西县| 塔城市| 崇阳县| 黄山市| 武乡县| 忻州市| 西宁市| 汕头市| 霞浦县| 九寨沟县| 治县。| 太原市| 江川县| 四平市| 琼结县| 曲麻莱县| 临朐县| 内乡县| 巴南区| 那曲县| 玛多县| 景洪市|