【JAVA】JAVA後端,JVM,併發之可見性與原子性,併發與鎖


1.Java後端

1.1 編程基礎

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

1.2 應用框架

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

1.3 工具運維

在這裏插入圖片描述
在這裏插入圖片描述

2.JVM

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

2.1 程序計數器

在這裏插入圖片描述
在這裏插入圖片描述

2.2 棧和棧幀

當調用第一個方法時,給第一個方法劃分一段棧幀空間並壓入棧內。當這個方法執行完了將這個方法對應的棧幀出棧也就是釋放這個方法所佔用的內存。一個棧內有沒有可能多個棧幀存在?有的,調用方法1,方法1間接調用方法2,爲方法2調用一塊內存入棧,方法2又調用了方法3(尾遞歸)。。。方法3調用結束就把棧幀3內存釋放掉。。。。
在這裏插入圖片描述
在這裏插入圖片描述
如下main調用method1,method1調用method2,活動棧幀就是棧頂部的正在執行的方法
在這裏插入圖片描述
棧內存就是一次次方法調用產生的棧幀內存,棧幀內存在每次方法結束後被彈出棧自動回收掉,不需要垃圾回收管理棧內存,垃圾回收只回收堆內存中無用對象。棧內存大小可通過運行代碼時虛擬機參數指定,棧內存劃的越大反而讓線程數變少:因爲物理內存大小一定的,比如一個線程使用了1M棧內存,總共物理內存有500M,理論上有500個同時運行。如果對每個線程的棧內存設置了2M,只能同時運行250個線程。所以棧內存不是分配越大越好,劃分大了只能更多次進行方法遞歸調用,不會增強運行效率反而影響到線程數目變少,一般採用系統默認棧內存大小就可以
在這裏插入圖片描述
如下解釋如上第三個問題
在這裏插入圖片描述
在這裏插入圖片描述
x變量是m1方法內的局部變量,我們說一個線程對應一個棧,線程內每次方法調用會產生一個新的棧幀,如下線程不會產生安全問題
在這裏插入圖片描述
如下不加線程保護的話會產生安全問題
在這裏插入圖片描述
如下只有第一個線程安全
在這裏插入圖片描述
棧幀過多:方法的遞歸調用沒有設置正確的計數條件導致遞歸爆棧。棧幀過大(很少出現):棧幀裏都是些局部變量,方法參數,佔用的內存都很小(一個int變量4字節,默認棧大小1M左右)
在這裏插入圖片描述
在這裏插入圖片描述
如下設置Xss棧大小256k,比默認小。如上代碼執行減少到5000多次
在這裏插入圖片描述
兩個類循環引用導致將java對象解析爲json字符串數據StackOverflowError
在這裏插入圖片描述

2.3 線程診斷

在這裏插入圖片描述
top命令只能定位到進程,無法定位到線程
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

2.4 堆和方法區

本地方法:不是由java編寫的方法。java不能與操系底層打交道,需要C/C++本地方法與os底層api打交道。java可間接通過本地方法調用底層功能。本地方法棧就是給本地方法提供的內存空間。程序計數器,虛擬機棧(只要局部變量不在逃逸出方法的作用範圍就是線程安全的),本地方法棧都是線程私有
堆和方法區是線程共享:堆中不再被引用的對象(這對象沒人再使用了,但是如果不斷產生對象有人用就不能作爲垃圾)當成垃圾進行回收釋放空閒的內存,不至於讓內存被創建的對象撐爆
在這裏插入圖片描述
在這裏插入圖片描述
方法區:存儲了類結構相關信息
在這裏插入圖片描述

3.併發之可見性與原子性

3.1 併發(線程)之可見性

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
如上線程2不能立即讀到線程1寫後的最新變量值,多線程不可見性
在這裏插入圖片描述
如何解決多線程不可見性:加volatile關鍵字使a在主存和localcache間強制刷新一致
在這裏插入圖片描述

3.2 原子性(讀寫原子)

如果線程1基於讀的變量再對變量進行寫,最典型操作i++,T1和T2都進行i++操作
在這裏插入圖片描述
一開始i=0,經過兩個線程兩次i++操作結果變成了1,這顯然是不對的,並且這種情況下不能用volatile保證這樣操作的正確性。。T1賦給i即使刷新主存中i=1,T2已經讀過0了。兩個線程既有讀操作,又有基於讀操作的寫操作,可見性只保證一個線程寫另一個線程讀是正確的,這裏可見性不適用。現在想做的是將讀操作和寫操作合爲一步,要麼同時發生要麼同時不發生(原子性)。在保證原子性同時一定以保證可見性爲前提的不是並列關係(AtomicInteger類裏本質上value值就是volatile的)。本身不可見的話沒辦法保證原子性
在這裏插入圖片描述
volatile不具有傳染性(用volatile修飾的對象的內部屬性不具有可見性,反之用volatile修飾的內部屬性也不能保證所在對象的可見性),volatile還有防止指令重排作用
在這裏插入圖片描述
用synchronized同步關鍵字來保證原子性發生,同步關鍵字同一時間只有一個線程進入代碼段
在這裏插入圖片描述
volatile可見性關鍵字最輕量級(保證一個線程寫,一個線程讀能讀到最新的值),AtomicInteger(保證既有讀操作又有寫操作如i++這種場景下能保證操作的原子性)基於volatile,synchronized最重量級(能保證整個代碼塊中所有操作都是原子性的)。多線程情況下需要自增請使用Atomicxxx類來實現

4.併發與鎖

關於內存見文章:https://blog.csdn.net/weixin_43435675/article/details/105901050
在這裏插入圖片描述
內存,cpu(由控制器和運算器組成,通過總線與其他設備連接),io是編程中三個最重要的點。南橋連接帶寬要求低的設備如是一些鼠標鍵盤硬盤usb設備等。北橋負責帶寬比較高的設備如pcie顯卡,pcie硬盤,內存這些都需要高速訪問的
在這裏插入圖片描述
在這裏插入圖片描述
如下是cpu狀態查看
在這裏插入圖片描述
如何利用cpu資源?外部資源利用都是通過操作系統提供的接口,os給了我們兩種抽象即進程和線程。進程是系統資源分配,調度和管理的最小單位,比如去任務管理器查看使用內存時是看的哪個進程或哪個程序使用了多少內存而不是哪個線程,如果是哪個線程根本不知道是哪個程序裏的線程沒法管理。一個進程的內存空間是一套完整的虛擬內存地址空間,這個進程中所有線程都共享這一套地址空間。線程是cpu運算的最小執行單位,也就是說真正去讓cpu執行任務時,cpu看到的就是線程了,cpu並不在意是哪個進程,cpu就是輪換着線程來運行並不需要知道這個線程是屬於哪個進程的。cpu和併發,線程纔是主角
在這裏插入圖片描述
nodejs用戶看是單線程能應對高併發,因爲io異步,回調函數形式接收io執行結果。但內核看是多線程,如下線程數12
在這裏插入圖片描述
如下是線程的5種狀態,只有運行中是佔用cpu資源的
在這裏插入圖片描述
線程在執行時有一定性能損耗,這些損耗來自線程的創建銷燬和切換,線程本質向cpu申請計算資源,用戶態轉內核態。os給我們提供的去獲取cpu資源形式就是線程,多線程面臨多線程切換問題不得避免
在這裏插入圖片描述
協程是用戶自定義線程但與os的線程不同,協程不進入內核態
在這裏插入圖片描述
synchronized是java中關鍵字
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章