原子性 - synchronized關鍵詞

原子性概念

原子性提供了程序的互斥操做,同一時刻只能有一個線程能對某塊代碼進行操做。java

原子性的實現方式

在jdk中,原子性的實現方式主要分爲:git

  • synchronized:關鍵詞,它依賴於JVM,保證了同一時刻只能有一個線程對做用對象做用範圍內進行操做。
  • Lock:代碼層面的鎖,依賴CPU指令,主要實現類ReentrantLock,以後再說。
  • atomic包:該包中提供了一些能夠保證原子操做的類。

注意上面加粗內容,比較關鍵,後面會有講解,着重理解多線程

從性能上看,上面三個逐優,但各自又有不一樣。函數

  • synchronized是不可中斷鎖,適合競爭不激烈,可讀性比較好。
  • Lock是可中斷鎖,多樣化同步,競爭激烈時能維持常態。
  • atomic競爭激烈時能維持常態,比Lock性能好,但只能同步一個值。

synchronized的四種用法

  • synchronized修飾的代碼塊,做用於調用的對象
  • synchronized修飾的方法,做用於調用的對象
  • synchronized修飾的靜態方法,做用於這個類的全部對象
  • synchronized修飾的類,做用於這個類的全部對象

synchronized修飾的代碼塊和修飾的方法

package cn.com.dotleo.sync;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by liufei on 2018/6/24.
 */
public class Sync1 {

    public void test1(int x) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.println("["+ x + "]test1: " + i);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static void main(String[] args) {
        final Sync1 sync = new Sync1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            public void run() {
                sync.test1(1);
            }
        });

        executorService.execute(new Runnable() {
            public void run() {
                sync.test1(2);
            }
        });
    }
}

代碼參見Sync1.java性能

上面代碼中,主要體現了多線程下,同一個對象調用同步代碼塊。this

運行結果:atom

[1]test1: 0
[1]test1: 1
[1]test1: 2
[1]test1: 3
[1]test1: 4
[1]test1: 5
[1]test1: 6
[1]test1: 7
[1]test1: 8
[1]test1: 9
[2]test1: 0
[2]test1: 1
[2]test1: 2
[2]test1: 3
[2]test1: 4
[2]test1: 5
[2]test1: 6
[2]test1: 7
[2]test1: 8
[2]test1: 9

由此結果能夠看出,在多線程下,兩個線程同時做用於一個對象時,該代碼塊被先搶到資源的進程執行結束後另外一個線程才得以執行。線程

將主函數修改以下:code

public static void main(String[] args) {

        final Sync1 sync1 = new Sync1();
        final Sync1 syncNew = new Sync1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            public void run() {
                sync1.test1(1);
            }
        });
        executorService.execute(new Runnable() {
            public void run() {
                syncNew.test1(2);
            }
        });


    }

代碼參見Sync1.java對象

[1]test1: 0
[2]test1: 0
[1]test1: 1
[2]test1: 1
[1]test1: 2
[2]test1: 2
[1]test1: 3
[2]test1: 3
[1]test1: 4
[2]test1: 4
[1]test1: 5
[2]test1: 5
[1]test1: 6
[2]test1: 6
[1]test1: 7
[2]test1: 7
[1]test1: 8
[2]test1: 8
[2]test1: 9
[1]test1: 9

從結果中能夠看出,對於兩個線程分別調用兩個對象時,並無鎖的出現,線程交叉執行並輸出。

同理,synchronized關鍵詞修飾方法也是如此,這裏再也不演示,代碼參見Sync2.java

所以咱們能夠看出:

  • synchronized修飾的代碼塊,做用於調用的對象
  • synchronized修飾的方法,做用於調用的對象

synchronized修飾的靜態方法和類

修飾靜態方法:

package cn.com.dotleo.sync;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by liufei on 2018/6/24.
 */
public class Sync3 {

    public static synchronized void test3(int x) {
        for (int i = 0; i < 10; i++) {
            System.out.println("["+ x + "]test3: " + i);
        }
    }

    public static void main(String[] args) {

        final Sync3 sync3 = new Sync3();
        final Sync3 syncNew = new Sync3();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            public void run() {
                sync3.test3(1);
            }
        });
        executorService.execute(new Runnable() {
            public void run() {
                syncNew.test3(2);
            }
        });
    }
}

代碼參見Sync3.java

關於synchronized修飾靜態方法和類再也不演示兩個線程調用同一個對象,由於那樣確定是加鎖的順序輸出。

[1]test3: 0
[1]test3: 1
[1]test3: 2
[1]test3: 3
[1]test3: 4
[1]test3: 5
[1]test3: 6
[1]test3: 7
[1]test3: 8
[1]test3: 9
[2]test3: 0
[2]test3: 1
[2]test3: 2
[2]test3: 3
[2]test3: 4
[2]test3: 5
[2]test3: 6
[2]test3: 7
[2]test3: 8
[2]test3: 9

修飾靜態類:

package cn.com.dotleo.sync;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by liufei on 2018/6/24.
 */
public class Sync4 {

    public static void test4(int x) {
        synchronized (Sync4.class) {
            for (int i = 0; i < 10; i++) {
                System.out.println("["+ x + "]test4: " + i);
            }
        }

    }

    public static void main(String[] args) {

        final Sync4 sync4 = new Sync4();
        final Sync4 syncNew = new Sync4();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            public void run() {
                sync4.test4(1);
            }
        });
        executorService.execute(new Runnable() {
            public void run() {
                syncNew.test4(2);
            }
        });
    }
}

代碼參見Sync4.java

[1]test4: 0
[1]test4: 1
[1]test4: 2
[1]test4: 3
[1]test4: 4
[1]test4: 5
[1]test4: 6
[1]test4: 7
[1]test4: 8
[1]test4: 9
[2]test4: 0
[2]test4: 1
[2]test4: 2
[2]test4: 3
[2]test4: 4
[2]test4: 5
[2]test4: 6
[2]test4: 7
[2]test4: 8
[2]test4: 9

從上面的兩個示例中均可以看出,在兩個線程調用一個類的不一樣對象時,仍然能保證原子性。

因而可知:

  • synchronized修飾的靜態方法,做用於這個類的全部對象
  • synchronized修飾的類,做用於這個類的全部對象

特別強調:

synchronized不是方法申明的一部分,若是子類繼承了父類的synchronized方法,除非子類不會有synchronized修飾的。

相關文章
相關標籤/搜索