Java線程之synchronized關鍵字

synchronized的原理

在java中synchronized是用來給對象、方法、或者代碼塊加上同步鎖的,通過synchronized可以在多線程中實現對對象、方法、或者代碼塊的互斥訪問。
例如,現在有兩個線程A和線程B,它們都會訪問“對象obj的同步鎖”。假設,在某一時刻,線程A獲取到“obj的同步鎖”並在執行一些操作; 而此時,線程B也企圖獲取“obj的同步鎖” , 線程B會獲取失敗,它必須等待,直到線程A釋放了“該對象的同步鎖”之後線程B才能獲取到“obj的同步鎖”從而纔可以運行。

synchronized的規則

一、當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。

二、當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。

三、當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。

通過代碼來驗證上面的三條規則:

當一個線程訪問對象中的synchronized(this)同步代碼塊時,其他線程對該代碼塊將阻塞:

    static  class MyThread implements Runnable{

        @Override
        public void run() {
            synchronized (this) {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+i);

                }
            }

        }

    }

    public static void main(String[] args) {

        Runnable runnable = new MyThread();

        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);

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

打印結果:

Thread-0:0
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
Thread-1:0
Thread-1:1
Thread-1:2
Thread-1:3
Thread-1:4
Thread-1:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9

MyThread的run方法中通過synchronized對代碼塊加了同步鎖,然後創建一個runnable對象,線程t1和t2都訪問runnable對象並調用它的run方法,可以理解爲t1和t2共享了這個對象的同步鎖,所以t2必須等到t1執行完成後才能執行。

將上面的代碼修改如下:

        Runnable runnable = new MyThread();
        Runnable runnable1 = new MyThread();

        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable1);

打印結果:

Thread-1:0
Thread-0:0
Thread-1:1
Thread-0:1
Thread-1:2
Thread-0:2
Thread-1:3
Thread-0:3
Thread-1:4
Thread-0:4
Thread-1:5
Thread-0:5
Thread-1:6
Thread-0:6
Thread-1:7
Thread-0:7
Thread-1:8
Thread-0:8
Thread-1:9
Thread-0:9

雖然加同步鎖的是同一個代碼塊,但因爲是訪問的不同的對象runnable和runnable1,t1和t2並沒有共享同步鎖,所以兩個線程同步執行。

當一個線程訪問對象的synchronized代碼塊時,另一個線程依然可以訪問它的非synchronized代碼塊

    static class Dog {
        public void running(){
            synchronized (this) {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("the dog is running:"+i);

                }
            }
        }

        public void breaking() {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("the dog is breaking:"+i);

            }
        }
    }

    public static void main(String[] args) {
        Dog dog = new Dog();

        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                dog.running();
            }
        });
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                dog.breaking();
            }
        });

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

打印結果:

the dog is running:0
the dog is breaking:0
the dog is running:1
the dog is breaking:1
the dog is breaking:2
the dog is running:2
the dog is breaking:3
the dog is running:3
the dog is running:4
the dog is breaking:4
the dog is running:5
the dog is breaking:5
the dog is running:6
the dog is breaking:6
the dog is running:7
the dog is breaking:7
the dog is running:8
the dog is breaking:8
the dog is running:9
the dog is breaking:9

上面的代碼創建了一個dog對象,它有兩個方法一個加了synchronized的running和一個普通的breaking方法。當t1去調用它的running方法時並沒有阻塞到t2去調用它沒有加同步鎖的方法。

當一個線程訪問對象的加了同步鎖的方法A時,另一個線程調用這個對象的另一個加了同步鎖的方法B時也會阻塞

將第二條中Dog的breaking方法也加上同步鎖如下:

public void breaking() {
    synchronized (this) {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("the dog is breaking:"+i);

        }
    }
}

然後依然用兩個線程去訪問:

        Dog dog = new Dog();

        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                dog.running();
            }
        });
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                dog.breaking();
            }
        });

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

打印結果:

the dog is running:0
the dog is running:1
the dog is running:2
the dog is running:3
the dog is running:4
the dog is running:5
the dog is running:6
the dog is running:7
the dog is running:8
the dog is running:9
the dog is breaking:0
the dog is breaking:1
the dog is breaking:2
the dog is breaking:3
the dog is breaking:4
the dog is breaking:5
the dog is breaking:6
the dog is breaking:7
the dog is breaking:8
the dog is breaking:9

可以看到只有當t1執行完runing方法後t2才能執行。

對象鎖和全局鎖

對象鎖:

將鎖添加在某一個對象身上,只需要synchronized

全局鎖:

只針對當前類,無論這個類產生多少個對象,這些對象都共享這個鎖,static synchronized

其實這兩個鎖結結合static關鍵字很容易理解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章