JAVA Synchronized

參考 / References :https://blog.csdn.net/mulanlong/article/details/84566016

最近刷leetcode時刷到了concurrency題目,所以深入學習下synchronized

1.概念
synchronized可以修飾代碼塊,方法,靜態類和類

2.修飾代碼塊:

public class SyncThread implements Runnable {

    private static int count;

    public SyncThread() {
        count = 0;
    }

    @Override
    public void run() {
        synchronized (this) {
            for(int i = 0; i < 5; ++i) {
                try {
                    System.out.println(Thread.currentThread().getName() + " : " + count++);
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
   
    public static void main(String[] args) {
        SyncThread thread = new SyncThread();
        Thread thread1 = new Thread(thread, "1");
        Thread thread2 = new Thread(thread, "2");
        thread1.start();
        thread2.start();
    }
}

結果輸出如下:

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

由於兩個子線程都是訪問同一個syncThread,所以必須等線程1訪問完,釋放對象鎖後,線程2才能訪問。

如果把代碼改成訪問兩個不同的線程:

public static void main(String[] args) {
        SyncThread thread = new SyncThread();
        SyncThread threadd = new SyncThread();
        Thread thread1 = new Thread(thread, "1");
        Thread thread2 = new Thread(threadd, "2");
        thread1.start();
        thread2.start();
    }

結果輸出如下:

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

可以看到線程1與線程2併發執行了。synchronized只鎖定對象,每個對象只有一個鎖與之關聯,所以修改後代碼中的兩把鎖不會形成互斥。
synchronized(this):代表使用類生成的對象作爲鎖,同時以對象爲阻塞。
synchronized(SyncThread.class): 代表使用類的字節碼作爲鎖,所有的對象都公用一個鎖,線程會阻塞。
修改後代碼如下:

@Override
    public void run() {
        synchronized (SyncThread.class) {
            for(int i = 0; i < 5; ++i) {
                try {
                    System.out.println(Thread.currentThread().getName() + " : " + count++);
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

結果輸出如下:

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

如果此時在此類中new一個對象鎖,代碼如下:

private Object lock = new Object();

    @Override
    public void run() {
        synchronized (lock) {
            for(int i = 0; i < 5; ++i) {
                try {
                    System.out.println(Thread.currentThread().getName() + " : " + count++);
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

運行結果如下:

2 : 0
1 : 1
1 : 2
2 : 3
1 : 5
2 : 4
1 : 7
2 : 6
1 : 8
2 : 8

可以看到由於new一個新的對象的時候,也同時new一個新的鎖,所以兩個線程並沒有公用一把鎖,因此線程會交替執行。如果線程分別來自兩個對象,還會出現數據異常(指的是兩個線程同時訪問count導致數據還沒來得及跟新)。

倘若上述代碼中的lock是static修飾的呢?
結果如下:

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

由於static有如下特性:
① static 修飾的成員(字段/方法),隨着所在類的加載而加載
當 JVM 把字節碼加載進入 JVM 的時候,static 修飾的成員已經在內存中了
② 優先於對象的存在
對象是被手動通過 new 關鍵字創造出來的
③ static 修飾的成員被該類型的所有對象所共享
根據該類創建出來的任何對象,都可以訪問 static 成員。

所以該static修飾的lock是所有對象共享的,因此必須等一個線程執行完,另一個線程才能執行。

3.可用volatile字段修飾private volatile int count;這樣可以鎖上全部,volatile修飾的關鍵字一旦修改會使得線程中的變量緩存失效,重新從數據中讀取。

4.還可以對函數上鎖:

private static int count;

    public SyncThread() {
        count = 0;
    }
    
    public synchronized int setCount() {
        this.count++;
        return count;
    }

    @Override
    public void run() {

            for(int i = 0; i < 5; ++i) {
                try {
                    System.out.println(Thread.currentThread().getName() + " : " + setCount());
                    Thread.sleep(100);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }

    public static void main(String[] args) {
        SyncThread thread = new SyncThread();
        SyncThread threadd = new SyncThread();
        Thread thread1 = new Thread(thread, "1");
        Thread thread2 = new Thread(threadd, "2");
        thread1.start();
        thread2.start();
    }

結果如下:

1 : 1
2 : 2
1 : 3
2 : 4
2 : 5
1 : 6
2 : 7
1 : 8
2 : 9
1 : 10
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章