Java中synchronized同步鎖用法及做用範圍

Java 中的 synchronized 關鍵字能夠在多線程環境下用來做爲線程安全的同步鎖。本文主要對 synchronized 的做用,以及其有效範圍進行討論。
Java中的對象鎖和類鎖:java的對象鎖和類鎖在鎖的概念上基本上和內置鎖是一致的,可是,兩個鎖實際是有很大的區別的,對象鎖是用於對象實例方法,或者一個對象實例上的,類鎖是用於類的靜態方法或者一個類的class對象上的。咱們知道,類的對象實例能夠有不少個,可是每一個類只有一個class對象,因此不一樣對象實例的對象鎖是互不干擾的,可是每一個類只有一個類鎖。可是有一點必須注意的是,其實類鎖只是一個概念上的東西,並非真實存在的,它只是用來幫助咱們理解鎖定實例方法和靜態方法的區別的。java

synchronized 關鍵字主要有如下幾種用法:
- 非靜態方法的同步;
- 靜態方法的同步;
- 代碼塊。安全

下面分對象鎖和類鎖來分別說明 synchronized 用法:多線程

對象鎖
非靜態方法使用 synchronized 修飾的寫法,修飾實例方法時,鎖定的是當前對象:ide

    public synchronized void test(){
        // TODO
    }
1
2
3
代碼塊使用 synchronized 修飾的寫法,使用代碼塊,若是傳入的參數是 this,那麼鎖定的也是當前的對象:測試

    public void test(){
        synchronized (this) {
            // TODO
        }
    }
1
2
3
4
5
下面經過例子來講明對象鎖:
定義一個類,方法以下,將 count 自減,從 5 到 0:this

public class TestSynchronized {.net

    public synchronized void minus() {
        int count = 5;
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + " - " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }線程

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
測試調用方法以下:對象

public class Run {blog

    public static void main(String[] args) {

        final TestSynchronized test = new TestSynchronized();

        Thread thread1 = new Thread(new Runnable() {

            @Override
            public void run() {
                test.minus();
            }
        });

        Thread thread2 = new Thread(new Runnable() {

            @Override
            public void run() {
                test.minus();
            }
        });

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

    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
兩個線程 thread1 和 thread2,同時訪問對象的方法,因爲該方法是 synchronized 關鍵字修飾的,那麼這兩個線程都須要得到該對象鎖,一個得到後另外一個線程必須等待。因此咱們能夠猜想運行結果應該是,一個線程執行完畢後,另外一個線程纔開始執行,運行例子,輸出打印結果以下:

Thread-0 - 4
Thread-0 - 3
Thread-0 - 2
Thread-0 - 1
Thread-0 - 0
Thread-1 - 4
Thread-1 - 3
Thread-1 - 2
Thread-1 - 1
Thread-1 - 0
1
2
3
4
5
6
7
8
9
10
(另:thread1 和 thread2 誰先執行並不必定)
本例對於對象鎖進行了基礎的解釋。可是對象鎖的範圍是怎樣的,對象的某個同步方法被一個線程訪問後,其餘線程能不能訪問該對象的其餘同步方法,以及是否能夠訪問對象的其餘非同步方法呢,下面對兩種進行驗證:

對兩個同步方法兩個線程的驗證:
修改類以下,加入 minus2() 方法,和 minus() 方法同樣:

package com.test.run;

public class TestSynchronized {

    public synchronized void minus() {
        int count = 5;
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + " - " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

    public synchronized void minus2() {
        int count = 5;
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + " - " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
測試調用以下,兩個線程訪問不一樣的方法:

public class Run {

    public static void main(String[] args) {

        final TestSynchronized test = new TestSynchronized();

        Thread thread1 = new Thread(new Runnable() {

            @Override
            public void run() {
                test.minus();
            }
        });

        Thread thread2 = new Thread(new Runnable() {

            @Override
            public void run() {
                test.minus2();
            }
        });

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
輸出結果以下:

Thread-0 - 4
Thread-0 - 3
Thread-0 - 2
Thread-0 - 1
Thread-0 - 0
Thread-1 - 4
Thread-1 - 3
Thread-1 - 2
Thread-1 - 1
Thread-1 - 0
1
2
3
4
5
6
7
8
9
10
能夠看到,某個線程獲得了對象鎖以後,該對象的其餘同步方法是鎖定的,其餘線程是沒法訪問的。
下面看是否能訪問非同步方法:
修改類代碼以下,將 minus2() 的 synchronized 修飾去掉,代碼以下:

public class TestSynchronized {

    public synchronized void minus() {
        int count = 5;
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + " - " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

    public void minus2() {
        int count = 5;
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + " - " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
測試調用的類不變,以下:

public class Run2 {

    public static void main(String[] args) {

        final TestSynchronized test = new TestSynchronized();

        Thread thread1 = new Thread(new Runnable() {

            @Override
            public void run() {
                test.minus();
            }
        });

        Thread thread2 = new Thread(new Runnable() {

            @Override
            public void run() {
                test.minus2();
            }
        });

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

    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
執行結果以下:

Thread-1 - 4
Thread-0 - 4
Thread-1 - 3
Thread-0 - 3
Thread-1 - 2
Thread-0 - 2
Thread-1 - 1
Thread-0 - 1
Thread-1 - 0
Thread-0 - 0
1
2
3
4
5
6
7
8
9
10
能夠看到,結果是交替的,說明線程是交替執行的,說明若是某個線程獲得了對象鎖,可是另外一個線程仍是能夠訪問沒有進行同步的方法或者代碼。進行了同步的方法(加鎖方法)和沒有進行同步的方法(普通方法)是互不影響的,一個線程進入了同步方法,獲得了對象鎖,其餘線程仍是能夠訪問那些沒有同步的方法(普通方法)。當獲取到與對象關聯的內置鎖時,並不能阻止其餘線程訪問該對象,當某個線程得到對象的鎖以後,只能阻止其餘線程得到同一個鎖。

類鎖
類鎖須要 synchronized 來修飾靜態 static 方法,寫法以下:

    public static synchronized void test(){
        // TODO
    }
1
2
3
或者使用代碼塊,需引用當前的類:

    public static void test(){
        synchronized (TestSynchronized.class) {
            // TODO
        }
    }
1
2
3
4
5
舉例說明類鎖的做用:

public class TestSynchronized {

    public static synchronized void minus() {
        int count = 5;
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + " - " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
測試調用類以下:

public class Run {

    public static void main(String[] args) {

        Thread thread1 = new Thread(new Runnable() {

            @Override
            public void run() {
                TestSynchronized.minus();
            }
        });

        Thread thread2 = new Thread(new Runnable() {

            @Override
            public void run() {
                TestSynchronized.minus();
            }
        });

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

    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
輸出結果以下:

Thread-0 - 4
Thread-0 - 3
Thread-0 - 2
Thread-0 - 1
Thread-0 - 0
Thread-1 - 4
Thread-1 - 3
Thread-1 - 2
Thread-1 - 1
Thread-1 - 0
1
2
3
4
5
6
7
8
9
10
能夠看到,類鎖和對象鎖實際上是同樣的,因爲靜態方法是類全部對象共用的,因此進行同步後,該靜態方法的鎖也是全部對象惟一的。每次只能有一個線程來訪問對象的該非靜態同步方法。
類鎖的做用和對象鎖相似,可是做用範圍是否和對象鎖一致呢,下面看對象鎖和類鎖是否等同:
修改類,兩個同步方法,其中一個是靜態的:

public class TestSynchronized {

    public static synchronized void minus() {
        int count = 5;
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + " - " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

    public synchronized void minus2() {
        int count = 5;
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + " - " + count);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
測試調用類以下,靜態方法直接用類調用,實例方法由對象來調用:

public class Run {

    public static void main(String[] args) {

        final TestSynchronized test = new TestSynchronized();

        Thread thread1 = new Thread(new Runnable() {

            @Override
            public void run() {
                TestSynchronized.minus();
            }
        });

        Thread thread2 = new Thread(new Runnable() {

            @Override
            public void run() {
                test.minus2();
            }
        });

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

    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
運行結果:

Thread-1 - 4 Thread-0 - 4 Thread-0 - 3 Thread-1 - 3 Thread-0 - 2 Thread-1 - 2 Thread-0 - 1 Thread-1 - 1 Thread-1 - 0 Thread-0 - 0 1 2 3 4 5 6 7 8 9 10 能夠看到兩個線程是交替進行的,也就是說類鎖和對象鎖是不同的鎖,是互相獨立的。 ———————————————— 版權聲明:本文爲CSDN博主「yx0628」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/yx0628/article/details/79086511

相關文章
相關標籤/搜索