天天看點

Java并發程式設計之原子性-synchronized關鍵字

原子性提供了互斥通路,同一個時刻隻能有一個線程對其進行操作,Java裡能夠保持原子性的除了atomic包之外還有鎖

synchronized關鍵字:主要依賴jvm來實作鎖,在這個關鍵字的作用對象的作用範圍内隻能有一個線程來執行操作。

Lock:jdk提供的代碼層面的鎖,依賴特殊的cpu指令,比較有代表性的有ReentrantLock

synchronized是一種同步鎖,它修飾的對象主要有四種:

修飾代碼塊(同步語句塊):大括号括起來的代碼,作用于調用的對象。

修飾方法(同步方法):整個方法,作用于調用的對象。

同步語句塊及同步方法的作用僅局限于調用的對象。即該鎖存在于同一個執行個體當中。示範如下:

@Slf4j
public class SynchronizedExample1 {

    // 修飾一個代碼塊
    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }


    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test1(1);
        });
        executorService.execute(() -> {
            example2.test1(2);
        });
    }
}
           

檢視執行結果如下:

Java并發程式設計之原子性-synchronized關鍵字

可以看到執行的結果是test1 0-9,test2 0-9。

分别建立兩個對象:

@Slf4j
public class SynchronizedExample1 {

    // 修飾一個代碼塊
    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }


    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example2.test2(2);
        });
    }
}
           

執行結果:

Java并發程式設計之原子性-synchronized關鍵字

可以看到線程一和二是亂序執行的。說明鎖隻存在于調用的對象裡。

同理修飾一個方法執行代碼及結果如下:

@Slf4j
public class SynchronizedExample1 {

    // 修飾一個方法
    public synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example1.test2(2);
        });
    }
}
           

執行結果如下:

Java并發程式設計之原子性-synchronized關鍵字

分别建立兩個對象:

@Slf4j
public class SynchronizedExample1 {

    // 修飾一個方法
    public synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example2.test2(2);
        });
    }
}
           

執行結果如下:

Java并發程式設計之原子性-synchronized關鍵字

修飾靜态方法:整個靜态方法,作用于所有對象。

修飾類:括号括起來的部分,作用于所有對象。

修飾類示例如下:

@Slf4j
public class  SynchronizedExample2 {

    // 修飾一個類
    public static void test1(int j) {
        synchronized (SynchronizedExample2.class) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }


    public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test1(1);
        });
        executorService.execute(() -> {
            example2.test1(2);
        });
    }
}
           

執行結果如下:

Java并發程式設計之原子性-synchronized關鍵字

修飾靜态方法示例如下:

@Slf4j
public class  SynchronizedExample2 {

    // 修飾一個靜态方法
    public static synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example2.test2(2);
        });
    }
}
           

執行結果如下:

Java并發程式設計之原子性-synchronized關鍵字

可以看到,即使是不同執行個體,它們之間仍然能順序執行。是以修飾靜态方法和修飾類,它作用于所有對象。

注意:子類繼承父類是帶不上synchronized關鍵字,synchronized是不屬于方法聲明的一部分。如果子類也想用synchronized需要自己顯示聲明。

synchronized、Lock、Atomic原子性對比:

synchronized:不可中斷鎖,一旦代碼執行到其作用範圍内是必須等待代碼執行完畢。适合競争不激烈,可讀性好。在競争激烈時,性能急劇下降。

Lock:可中斷鎖,多樣化同步,比如有時間限制同步。競争不激烈時,性能稍比synchronized差。競争激烈時能夠維持常态(對于相同的代碼,不斷提高請求(或并發)時,平均處理速度上變化不大)

Atomic包:競争不激烈時,性能稍比synchronized差,競争激烈時能維持常态。比lock性能好,但是隻能同步一個值。

是以,寫同步的時候,優先考慮synchronized,如果有特殊需要,再進一步優化。ReentrantLock和Atomic如果用的不好,不僅不能提高性能,還可能帶來災難。 

想要檢視各測試結果,可以參看https://blog.csdn.net/z69183787/article/details/48420643

繼續閱讀