資安

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

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

一.友情鏈接

目錄
可直接運行的完整代碼
視頻講解
文字版

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

二.概述

多任務處理在現代計算機操作系統中幾乎已是一項必備的功能了。在許多場景下,讓計算機同時 去做幾件事情,不僅是因為計算機的運算能力強大了,還有一個很重要的原因是計算機的運算速度與 它的存儲和通信子系統的速度差距太大,大量的時間都花費在磁盤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判斷沒有競爭關係,則進行鎖消除處理,在該鎖被其它線程獲取時依據輕量鎖標記判斷退化為輕量鎖還是重量鎖

偏向鎖和輕量鎖的關係

Leave a Reply

Your email address will not be published. Required fields are marked *