1. 程式人生 > >synchronized修飾普通方法,修飾靜態方法比較

synchronized修飾普通方法,修飾靜態方法比較

一、synchronized修飾普通方法

初始化一個物件時,會自動生成一個與該物件對應的物件鎖,被synchronized 修飾的方法就得依靠物件鎖工作。當多執行緒同時訪問某個被synchronized修飾的方法時,一旦某個程序搶得物件鎖之後,其他的程序只有排隊對待。

例如,Person類裡有2個被synchronized 修飾的方法,…還是看程式碼吧:

public  class Person {

    public synchronized void eatApple() {
        Log.d("TAG", "eatApple begin at:" + System.currentTimeMillis());
        SystemClock.sleep(3000
); Log.d("TAG", "eatApple end at:" + System.currentTimeMillis()); } public synchronized void eatOrange() { for (int i = 0; i < 10; i++) { Log.d("TAG", "eatOrange is running"); SystemClock.sleep(200); } } }
public class MainActivity extends
AppCompatActivity {
Person person = new Person(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); thread1.start(); thread2.start(); } Thread thread1 = new
Thread(new Runnable() { @Override public void run() { person.eatApple(); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { person.eatOrange(); } }); }

執行結果:

12-14 16:26:48.243 14867-14920/com.example.shengqf.myapplication D/TAG: eatApple begin at:1513240008242
12-14 16:26:51.244 14867-14920/com.example.shengqf.myapplication D/TAG: eatApple end at:1513240011243
12-14 16:26:51.246 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:51.447 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:51.648 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:51.848 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:52.049 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:52.250 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:52.451 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:52.652 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:52.853 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:26:53.055 14867-14921/com.example.shengqf.myapplication D/TAG: eatOrange is running

結論:因為thread1中的eatApple()方法和thread2中的eatOrange()方法都是通過person物件呼叫的,他們用的是同一個物件鎖,互斥。所以,thread2一直等到thread1中的eatApple()方法執行完釋放了物件鎖後才開始執行其eatOrange()方法。

現在將上面thread2程式碼改一下:

  Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            Person person2 = new Person();
            person2.eatOrange();
        }
    });

再執行,結果:


12-14 16:43:05.011 16294-16320/com.example.shengqf.myapplication D/TAG: eatApple begin at:1513240985011
12-14 16:43:05.011 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:05.211 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:05.412 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:05.612 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:05.813 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:06.014 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:06.215 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:06.416 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:06.617 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:06.818 16294-16321/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-14 16:43:08.012 16294-16320/com.example.shengqf.myapplication D/TAG: eatApple end at:1513240988011

因為thread1中的eatApple()方法是通過person物件呼叫的,而thread2中的eatOrange()方法是通過person2物件呼叫的,他們用的是兩個不同的物件鎖,並不互斥。所以,thread2中的eatOrange()方法是和thread1中的eatApple()方法同時進行的。

結論:使用“同一個物件”去呼叫其被synchronized 修飾的普通方法才互斥。

上面的Person類等效於:

public class Person{

    public void eatApple() {
        synchronized(Person.this){
            Log.d("TAG", "eatApple begin at:" + System.currentTimeMillis());
            SystemClock.sleep(3000);
            Log.d("TAG", "eatApple end at:" + System.currentTimeMillis());
        }
    }

    public void eatOrange() {
        synchronized (Person.this) {
            for (int i = 0; i < 10; i++) {
                Log.d("TAG", "eatOrange is running");
                SystemClock.sleep(200);
            }
        }
    }
}

二、synchronized修飾靜態方法

直接看程式碼:

public class Person {

    public synchronized static void eatApple() {
        Log.d("TAG", "eatApple begin at:" + System.currentTimeMillis());
        SystemClock.sleep(3000);
        Log.d("TAG", "eatApple end at:" + System.currentTimeMillis());
    }

    public synchronized static void eatOrange() {
        for (int i = 0; i < 10; i++) {
            Log.d("TAG", "eatOrange is running");
            SystemClock.sleep(200);
        }
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        thread1.start();
        thread2.start();
    }

    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            Person.eatApple();
        }
    });

    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            Person.eatOrange();
        }
    });

}

執行結果:

12-15 09:09:24.378 31625-31912/com.example.shengqf.myapplication D/TAG: eatApple begin at:1513300164378
12-15 09:09:27.379 31625-31912/com.example.shengqf.myapplication D/TAG: eatApple end at:1513300167379
12-15 09:09:27.381 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:27.582 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:27.783 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:27.984 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:28.184 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:28.385 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:28.585 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:28.786 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:28.986 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:09:29.188 31625-31913/com.example.shengqf.myapplication D/TAG: eatOrange is running

現在將thread1和thread2程式碼改為:

Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            Person person1 = new Person();
            person1.eatApple();
        }
    });

    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            Person person2 = new Person();
            person2.eatOrange();
        }
    });

再執行結果:

12-15 09:12:38.316 32353-32378/com.example.shengqf.myapplication D/TAG: eatApple begin at:1513300358316
12-15 09:12:41.317 32353-32378/com.example.shengqf.myapplication D/TAG: eatApple end at:1513300361317
12-15 09:12:41.320 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:41.521 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:41.721 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:41.922 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:42.123 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:42.324 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:42.525 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:42.727 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:42.928 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running
12-15 09:12:43.129 32353-32379/com.example.shengqf.myapplication D/TAG: eatOrange is running

因為eatApple()和eatOrange()方法都是Person類裡的靜態方法,且被synchronized修飾,所以這兩個方法都是靠唯一的Person類鎖工作,所以無論是通過Person類名呼叫還是new不同的person物件呼叫,兩個方法都是互斥的。

結論:被synchronized 修飾的靜態方法要靠類鎖工作。當多執行緒同時訪問某個被synchronized修飾的靜態方法時,一旦某個程序搶得該類的類鎖之後,其他的程序只有排隊對待。

上面的Person程式碼等效於:

public class Person {

    public static void eatApple() {
        synchronized(Person.class){
            Log.d("TAG", "eatApple begin at:" + System.currentTimeMillis());
            SystemClock.sleep(3000);
            Log.d("TAG", "eatApple end at:" + System.currentTimeMillis());
        }
    }

    public static void eatOrange() {
        synchronized(Person.class){
            for (int i = 0; i < 10; i++) {
                Log.d("TAG", "eatOrange is running");
                SystemClock.sleep(200);
            }
        }
    }
}