網頁

2017/10/18

Java synchronized 同步的意思

在Java多執行緒中,可使用synchronized關鍵字用來宣告一個method或一段程式區塊為同步。同步的意思是,在同步方法/區塊/成員變數中一次只允許一條執行緒存取被鎖定的物件。

例如下面範例t1t2執行緒的run()方法中皆宣告了同步區塊,鎖定對象是傳入synchronizedpl物件,因此一次只會有一條執行緒(t1 or t2)存取pl物件。

public class Main {

    public static void main(String[] args) throws InterruptedException {

        PrintLoop pl = new PrintLoop();

        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                synchronized (pl) {
                    System.out.println(threadName + ":同步開始");
                    pl.print(threadName, 5);
                    System.out.println(threadName + ":同步結束");
                }
            }
        };

        Thread t2 = new Thread("t2") {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                synchronized (pl) {
                    System.out.println(threadName + ":同步開始");
                    pl.print(threadName, 5);
                    System.out.println(threadName + ":同步結束");
                }
            }
        };

        t1.start();
        t2.start();

    }

}

class PrintLoop {

    public void print(String threadName, int times) {
        System.out.println(threadName + ":print開始");
        for (int i = 0; i < times; i++) {
            System.out.println(threadName + ":" + i);
        }
        System.out.println(threadName + ":print結束");
    }

}

執行結果可能如下

t1:同步開始
t1:print開始
t1:0
t1:1
t1:2
t1:3
t1:4
t1:print結束
t1:同步結束
t2:同步開始
t2:print開始
t2:0
t2:1
t2:2
t2:3
t2:4
t2:print結束
t2:同步結束

如果把run()中的同步區塊移除,則pl將不被鎖定,可允許多個執行緒同時存取。

public class Main {

    public static void main(String[] args) throws InterruptedException {

        PrintLoop pl = new PrintLoop();

        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
//                synchronized (pl) {
                    System.out.println(threadName + ":同步開始");
                    pl.print(threadName, 5);
                    System.out.println(threadName + ":同步結束");
//                }
            }
        };

        Thread t2 = new Thread("t2") {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
//                synchronized (pl) {
                    System.out.println(threadName + ":同步開始");
                    pl.print(threadName, 5);
                    System.out.println(threadName + ":同步結束");
//                }
            }
        };

        t1.start();
        t2.start();

    }

}

class PrintLoop {

    public void print(String threadName, int times) {
        System.out.println(threadName + ":print開始");
        for (int i = 0; i < times; i++) {
            System.out.println(threadName + ":" + i);
        }
        System.out.println(threadName + ":print結束");
    }

}

則執行結果可能如下。

t1:同步開始
t2:同步開始
t1:print開始
t2:print開始
t1:0
t2:0
t1:1
t2:1
t1:2
t1:3
t1:4
t1:print結束
t2:2
t1:同步結束
t2:3
t2:4
t2:print結束
t2:同步結束

比較以上兩種不同的結果可觀察到有同步和沒同步的差異。在同步區塊中,被鎖定的物件只能被一條執行緒存取,直到執行緒離開同步區塊後才會釋放該物件的鎖,接著另一條執行緒才能取得鎖並存取該物件。

沒有留言:

張貼留言