1. 程式人生 > 其它 >Java併發15:併發三特性-有序性定義、有序性問題與有序性保證技術

Java併發15:併發三特性-有序性定義、有序性問題與有序性保證技術

在Java併發程式設計中,如果要保證程式碼的安全性,則必須保證程式碼的原子性、可見性和有序性。

在 Java併發12:併發三特性-原子性、可見性和有序性概述及問題示例中,對併發中的三個特性(原子性、可見性和有序性)進行了初步學習。
本章主要就Java中保障有序性的技術進行更加全面的學習。

1.整體回顧
有序性定義:即程式執行的順序按照程式碼的先後順序執行。
Java自帶有序性:happens-before原則。
2.有序性問題
其他大牛們經常拿下面的程式碼作為有序性的示例:

//執行緒1:
context = loadContext();   //語句1
inited = true;             //
語句2 //執行緒2: while(!inited ){ sleep() } doSomethingwithconfig(context);

不過本人並沒有通過實際程式設計執行,來證明此段程式的無序性。

為了更形象的理解有序性問題,本人使用了後面的示例,雖然後面的示例對有序性體現不夠準確。

如果各位看官,有更好的能夠實際體現有序性問題的示例,請一定告知,十分感謝!

場景說明:

有兩個執行緒A和執行緒B。
執行緒A對變數x進行加法和減法操作。
執行緒B對變數x進行乘法和出發操作。
程式碼:
這裡的示例只是為了方便得到無序的結果而專門寫到,所以有些奇特。

static String a1 = new
String("A : x = x + 1"); static String a2 = new String("A : x = x - 1"); static String b1 = new String("B : x = x * 2"); static String b2 = new String("B : x = x / 2"); //不採取有序性措施,也沒有發生有序性問題..... LOGGER.info("不採取措施:單執行緒序列,視為有序;多執行緒交叉序列,視為無序。"); new Thread(() -> { System.out.println(a1); try
{ Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(a2); }).start(); new Thread(() -> { System.out.println(b1); System.out.println(b2); }).start();

執行結果:

2018-03-18 00:16:20 INFO  ConcurrentOrderlyDemo:63 - 不採取措施:單執行緒序列,視為有序;多執行緒交叉序列,視為無序。
A : x = x + 1
B : x = x * 2
B : x = x / 2
A : x = x - 1

通過執行結果發現,多執行緒環境中,程式碼是交替的序列執行的,這樣會導致產生意料之外的結果。

3.有序性技術保障
在Java中提供了多種有序性保障措施,這裡主要涉及兩種:

通過synchronized關鍵字定義同步程式碼塊或者同步方法保障有序性。
通過Lock介面保障有序性。
3.1.synchronized關鍵字
定義一個物件用於同步塊:

//定義一個物件用於同步塊
static byte[] obj = new byte[0];

在多執行緒環境中進行synchronized關鍵字的有序性測試:

LOGGER.info("通過synchronized保證有序性:成功");
//通過synchronized保證有序性
new Thread(() -> {
    synchronized (obj) {
        System.out.println(a1);
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(a2);
    }
}).start();
new Thread(() -> {
    synchronized (obj) {
        System.out.println(b1);
        System.out.println(b2);
    }
}).start();

執行結果(多次):

2018-03-18 11:02:25 INFO  ConcurrentOrderlyDemo:79 - 通過synchronized保證有序性:成功
A : x = x + 1
A : x = x - 1
B : x = x * 2
B : x = x / 2

通過多次執行,發現執行結果一致,所以可以確定synchronized關鍵字能夠保證程式碼的有序性。

3.2.Lock介面

定義一個Lock鎖:
//定義一個Lock鎖 
static ReentrantLock reentrantLock = new ReentrantLock(true);

在多執行緒環境中進行Lock介面的有序性測試:

LOGGER.info("通過Lock保證有序性:成功");
//通過Lock保證有序性
new Thread(() -> {
    reentrantLock.lock();
    System.out.println(a1);
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(a2);
    reentrantLock.unlock();
}).start();
new Thread(() -> {
    reentrantLock.lock();
    System.out.println(b1);
    System.out.println(b2);
    reentrantLock.unlock();
}).start();

執行結果(多次):

2018-03-18 11:03:34 INFO  ConcurrentOrderlyDemo:100 - 通過Lock保證有序性:成功
A : x = x + 1
A : x = x - 1
B : x = x * 2
B : x = x / 2

通過多次執行,發現執行結果一致,所以可以確定Lock介面能夠保證程式碼的有序性。

4.總結

經驗證,以下兩種措施,可以保證Java程式碼在執行時的有序性:

  • synchronized關鍵字
  • Lock介面

併發三特性總結

特性 volatile關鍵字 synchronized關鍵字 Lock介面 Atomic變數
原子性 無法保障 可以保障 可以保障 可以保障
可見性 可以保障 可以保障 可以保障 可以保障
有序性 一定程度保障 可以保障 可以保障 無法保障