1. 程式人生 > 實用技巧 >偏向鎖、輕量鎖、重量鎖到底是啥?

偏向鎖、輕量鎖、重量鎖到底是啥?

偏向鎖、輕量鎖、重量鎖到底是啥?

一.友情連結

目錄
可直接執行的完整程式碼
視訊講解
文字版

如果有幫助到你的話請順手點個贊、加個收藏這對我真的很重要。別下次一定了,都不關注上哪下次一定。

二.概述

多工處理在現代計算機作業系統中幾乎已是一項必備的功能了。在許多場景下,讓計算機同時 去做幾件事情,不僅是因為計算機的運算能力強大了,還有一個很重要的原因是計算機的運算速度與 它的儲存和通訊子系統的速度差距太大,大量的時間都花費在磁碟I/O、網路通訊或者資料庫訪問上。這個在 一行程式碼完成多執行緒有寫過如何分配執行緒,原理基本一致。
由於計算機 的儲存裝置與處理器的運算速度有著幾個數量級的差距,所以現代計算機系統都不得不加入一層或多 層讀寫速度儘可能接近處理器運算速度的快取記憶體(Cache) 來作為記憶體與處理器之間的緩衝,但同時引入新的問題即快取一致性Java虛擬機器的即時編譯器中也有指令重排序 (Instruction Reorder)優化(

詳見)。

三.java記憶體模型

3.1主記憶體與工作記憶體

主記憶體直接對應於物理硬體的記憶體,而為了獲取更好的執行速度,虛擬機器(或者是硬體、作業系統本身的優化措施)可能會讓工作記憶體優先儲存於暫存器和快取記憶體中,因為程式執行時主要訪問的是工作記憶體。

3.2記憶體間互動操作

Java記憶體模型中定義了以下8種操作來完成:lock(鎖定)\unlock(解鎖)\read(從主記憶體讀取到工作記憶體中)\load(放入工作記憶體副本)\use(載入執行引擎)\assign(賦值)\store(傳遞至主記憶體)\write(寫入主記憶體)。即不允許read和load、store和write操作之一單獨出現。對一個變數執行unlock操作之前,必須先把此變數同步回主記憶體中(執行store、write操作)等

3.3volatile

可以被認為是輕量級同步,僅保證可見性,如下程式碼則會發現執行緒2永遠不會列印hello,但是當加入volatile就可以列印了

/**
 * @author seal email:[email protected]
 * @date 2020/11/22 3:20 PM
 * @description
 */
public class Concurrence {
    public  boolean v = false;
    // public volatile boolean v = false;
    public static void main(String[] args) {
        Concurrence c = new Concurrence();
        Thread thread1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            c.v = true;
            System.out.println(c.v);
        });
        Thread thread2 = new Thread(() -> {
            while (true){
                if (c.v) {
                    System.out.println("hello");
                }
            }
        });
        thread2.start();
        thread1.start();
        while (true) {

        }
    }
}

3.4long和double型變數的特殊規則

因為過去對於這兩個值特別的儲存方式使其並非原子性,但在JDK9以後可以使用-XX:+AlwaysAtomicAccesses開啟

3.5原子性、可見性與有序性

原子性:java記憶體模型中的除lock和unlock外其他6個均具備原子性,synchronized為大範圍原子性操作。
可見性:除了volatile之外,Java還有兩個關鍵字能實現可見性,它們是synchronized和final.
有序性:volatile和synchronized

四.java與執行緒

4.1核心執行緒

程式一般不會直接使用核心執行緒,而是使用核心執行緒的一種高階介面——輕量級程序(Light Weight Process,LWP),輕量級程序就是我們通常意義上所講的執行緒

4.2執行緒排程

排程主要方式有兩種,分別是協同式(Cooperative Threads-Scheduling)執行緒排程和搶佔式(Preemptive Threads-Scheduling)執行緒排程。但前者並不穩定。

4.3狀態轉換

6種執行緒狀態:新建(new)\執行(start)\無限期等待(需被顯式喚醒,主要有wait、join未設定引數)\限期等待(sleep、wait、join等)、阻塞(synchronized)、結束(shutdown)

五.java與協程

5.1使用者執行緒

使用者執行緒需要自行維護不如核心執行緒方便使用,但這部分可以交由虛擬機器完成,目前仍在fork/join中研發。研發成熟可以讓執行緒切換消耗更低的資源,而且更加輕量僅佔1MB

六.執行緒安全

6.1Java語言中的執行緒安全

6.1.1不可變

String、final以及基本資料型別

6.1.2絕對執行緒安全

synchronized修飾

6.1.3相對執行緒安全

例如vector、HashTable等使用同步手段

6.2執行緒安全的實現方法

6.2.1互斥同步

synchronized以及JDK5後新起的java.util.concurrent.locks.Lock

6.2.非阻塞同步

即CAS等實現

七.鎖優化(AQS)

7.1自旋鎖與自適應自旋

即有限次數的while(true),優點是避免執行緒切換

7.2鎖消除

例如某個同步方法中全程使用執行緒安全方法

7.3鎖粗化

例如在迴圈中不停加鎖解鎖,則直接會移到迴圈外面

7.4輕量級鎖

如果整個過程jvm判斷只有無競爭但有多個執行緒可能會使用時進行替換,由CAS實現,如果競爭過多則變為重量鎖

7.5偏向鎖

如果整個過程jvm判斷沒有競爭關係,則進行鎖消除處理,在該鎖被其它執行緒獲取時依據輕量鎖標記判斷退化為輕量鎖還是重量鎖