1. 程式人生 > 實用技巧 >執行緒同步(鎖的概念)

執行緒同步(鎖的概念)

首先先提供執行緒生命週期圖,方便理解

執行緒同步

執行緒同步機制簡介

  1. 執行緒同步機制是一套用於協調執行緒之間的資料訪問的機制.該機制可以保障執行緒安全.
  2. Java 平臺提供的執行緒同步機制包括: , volatile 關鍵字, final 關鍵字,static 關鍵字,以及相關的 API,Object.wait()/Object.notify()

鎖概述

  1. 執行緒安全問題的產生前提是多個執行緒併發訪問共享資料.將多個執行緒對共享資料的併發訪問轉換為序列訪問,即一個共享資料一次只能被一個執行緒訪問.鎖就是複用這種思路來保障執行緒安全的
  2. (Lock)可以理解為對共享資料進行保護的一個許可證. 對於同一個許可證保護的共享資料來說,任何執行緒想要訪問這些共享資料必須先持有該許可證. 一個執行緒只有在持有許可證的情況下才能對
    這些共享資料進行訪問; 並且一個許可證一次只能被一個執行緒持有; 持有許可證的執行緒在結束對共享資料的訪問後必須釋放其持有的許可證.
  3. 一執行緒在訪問共享資料前必須先獲得鎖; 獲得鎖的執行緒稱為鎖的持有執行緒; 一個鎖一次只能被一個執行緒持有. 鎖的持有執行緒在獲得鎖之後 和釋放鎖之前這段時間所執行的程式碼稱為臨界區(CriticalSection),鎖具有排他性(Exclusive), 即一個鎖一次只能被一個執行緒持有.這種鎖稱為排它鎖或互斥鎖(Mutex)


鎖的分類

JVM 把鎖分為內部鎖和顯示鎖兩種. 內部鎖通過 synchronized關鍵字實現; 顯示鎖通過 java.concurrent.locks.Lock 介面的實現類實現的

鎖的作用

  1. 鎖可以實現對共享資料的安全訪問. 保障執行緒的原子性,可見性與有序性
  2. 鎖是通過互斥保障原子性. 一個鎖只能被一個執行緒持有, 這就保證臨界區的程式碼一次只能被一個執行緒執行.使得臨界區程式碼所執行的操作自然而然的具有不可分割的特性,即具備了原子性.
  3. 可見性的保障是通過寫執行緒沖刷處理器的快取和讀執行緒重新整理處理器快取這兩個 動作實現的. java 平臺中,鎖的獲得隱含著重新整理處理器快取的動作, 鎖的釋放隱含著沖刷處理器快取的動作.
  4. 鎖能夠保障有序性.寫執行緒在臨界區所執行的在讀執行緒所執行的臨界區看來像是完全按照原始碼順序執行的.

注意:使用鎖保障執行緒的安全性,必須滿足以下條件:

  1. 這些執行緒在訪問共享資料時必須使用同一個鎖
  2. 即使是讀取共享資料的執行緒也需要使用同步鎖

鎖的相關概念

可重入性

可重入性(Reentrancy)描述這樣一個問題: 一個執行緒持有該鎖的時候能再次(多次)申請該鎖 .如果一個執行緒持有一個鎖的時候還能夠繼續成功申請該鎖,稱該鎖是可重入的, 否則就稱該鎖為不可重入的


鎖的爭用與排程

Java 平臺中內部鎖屬於非公平鎖, 顯示 Lock 鎖既支援公平鎖又支持非公平鎖

鎖的粒度

一個鎖可以保護的共享資料的數量大小稱為鎖的粒度.鎖保護共享資料量大,稱該鎖的粒度粗, 否則就稱該鎖的粒度細.鎖的粒度過粗會導致執行緒在申請鎖時會進行不必要的等待.鎖的粒度過細會增加鎖排程的開銷.

內部鎖:synchronized 關鍵字

Java 中的每個物件都有一個與之關聯的內部鎖(Intrinsic lock). 這種鎖也稱為監視器(Monitor), 這種內部鎖是一種排他鎖,可以保障原子性,可見性與有序性.內部鎖是通過 synchronized 關鍵字實現的.synchronized 關鍵字修
飾程式碼塊,修飾該方法.

語法

修飾程式碼塊的語法:
synchronized( 物件鎖 ) {
    同步程式碼塊,可以在同步程式碼塊中訪問共享資料
}
修飾例項方法就稱為同步例項方法
修飾靜態方法稱稱為同步靜態方法 

具體使用推薦動力節點的多執行緒教程

輕量級同步機制:volative 關鍵字

volatile 的作用

volatile 關鍵的作用使變數在多個執行緒之間可見【保證了執行緒的可見性,無法保證原子性】. (volatile 的作用可以強制執行緒從公共記憶體中讀取變數的值,而不是從工作內
存中讀取

語法

volatile public static int count;//在共享變數前面加一個volatile關鍵字即可

具體使用推薦動力節點的多執行緒教程

CAS

CAS概念

  1. CAS(Compare And Swap)是由硬體實現的.
  2. CAS 可以將 read- modify - write 這類的操作轉換為原子操作.
    i++自增操作包括三個子操作:從主記憶體讀取 i 變數值——》對 i 的值加 1——》再把加 1 之後 的值儲存到主記憶體

CAS 原理

在把資料更新到主記憶體時,再次讀取主記憶體變數的值,如果現在變數的值與期望的值(操作起始時讀取的值)一樣就更新.如圖

使用 CAS 實現執行緒安全的計數器

package com.edu.volatilekw;

class CASCounter {
    //使用 volatile 修飾 value 值,使執行緒可見
    volatile private long value;

    public long getValue() {
        return value;
    }

    //定義 comare and swap 方法
    private boolean compareAndSwap(long expectedValue, long newValue) {
        //如果當前 value 的值與期望的 expectedVAlue 值一樣,就把當前的 Value 欄位替換為newValue 值
        synchronized (this) {
            if (value == expectedValue) {
                value = newValue;
                return true;
            } else {
                return false;
            }
        }
    }

    //定義自增的方法
    public long incrementAndGet() {
        long oldvalue;
        long newValue;
        do {
            oldvalue = value;
            newValue = oldvalue + 1;
        } while (!compareAndSwap(oldvalue, newValue));
        return newValue;
    }
}

一些常見的原子變數類

原理:

原子變數類基於CAS實現的, 當對共享變數進行read-modify-write更新操作時,通過原子變數類可以保障操作的原子性與可見性.對變數的 read-modify-write 更新操作是指當前操作不是一個簡單的賦值,而是變數的新值依賴變數的舊值,如自增操作i++. 由於volatile只能保證
可見性,無法保障原子性, 原子變數類內部就是藉助一個 Volatile 變數,並且保障了該變數的 read-modify-write 操作的原子性, 有時把原子變數類看作增強的 volatile 變數.

原子變數類分類

分組
原子變數類
基礎資料型
AtomicInteger, AtomicLong, AtomicBoolean
陣列型
AtomicIntegerArray,
AtomicLongArray,AtomicReferenceArray
欄位更新器

AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,
AtomicReferenceFieldUpdater
引用型
AtomicReference, AtomicStampedReference,
AtomicMarkableReference

具體用法

下載jdk1.8.chm檔案(提取碼:5vby),進行檢視