Java學習總結 1-1-1 Java程序原理分析

筆記記錄,整理的亂七八糟~~

 

--> Java程序(class文件):

        Java源碼編譯成.class文件,class文件包含JAVA程序執行的字節碼;數據嚴格按照格式緊湊排列在class文件中的爲二進制流,中間無任何分隔符;文件開頭有一個0xcafebabe(16進制)特殊的標誌
        每個class文件包含:
        
            1.版本                --|
            2.訪問標誌            |
            3.常量池               |
            4.當前類               |
            5.超級類               |-->class文件是有複雜的格式專門給JVM讀取,可藉助工具查看
            6.接口                  |
            7.字段                  |
            8.方法                  |
            9.屬性               --|
        

--> JVM運行時數據區

        
        JVM(運行時數據區)用來存儲加載的類信息、常量、靜態變量、編譯後的數據

        最終由JVM執行引擎、本地庫接口和本地方法庫執行

        讀取class文件加載到JVM會分配不同的數據區:
                
      

                 
                
        線程共享部分:所有線程都可以訪問這塊內存數據,隨虛擬機或GC而創建或銷燬     

        線程獨佔部分:每個線程有獨立的空間,隨線程生命週期而創建或銷燬。JVM創建線程來執行代碼    
                
        方法區:存儲已被Java虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等
        
        堆內存:被所有線程共享的一塊內存區域,在虛擬機啓動時創建。唯一目的就是存放對象實例。所有的對象實例及數組都                           要在堆(Java Heap)上分配內存空間(GC的垃圾回收指的就是堆內存)
        
        虛擬機棧:每個線程在這個空間有一個私有空間。線程棧由多個棧幀(Stack Frame)組成。一個線程會執行一個或多個方                                       法,一個方法對應一個棧幀↓
                         棧幀內容包含:局部變量、操作數棧、動態鏈接、方法返回地址、附加信息等。佔內存默認最大1M,超出大小                                                               則拋出StackOverflowError
        

        本地方法棧:和虛擬機棧功能類似,虛擬機棧是爲虛擬機執行JAVA方法準備的,本地方法棧是爲虛擬機使用Native本地方                                 法而準備的。虛擬機規範沒有規定具體的實現,由不同的虛擬機廠商去實現。HotSpot虛擬機中兩棧實現方式                                         是一樣的,超出大小則拋出StackOverflowError
        
        程序計數器(Program Counter Register):程序計數器(PC)存的是指令的地址,程序運行第一條指令送入PC,cpu按照
                            PC的指定讀取第一條指令。當執行指令時cpu修改PC內容,每執行一條指令向PC增加一個量,使PC總是指向
                           下一條需要執行的指令地址,如果執行Native方法,則計數器爲空。每個線程都在這個空間有一個私有的空間,
                           佔用內存空間很少。CPU同一時間,只會執行一條線程中的指令。JVM多線程會輪流切換並分配CPU執行時間 
                            的方式。爲了線程切換後需要通過程序計數器來恢復正確的執行位置
        
        虛擬機規範中這是一個邏輯規劃區。具體實現根據不同悉尼及來實現↓
        例:oracle的HotSpot在Java7中方法去放在永久代,java8放在元數據空間,並且通過GC機制對這個區域進行管理
        
        
--> 線程

    狀態:
        線程有6個狀態定義:java.lang.Thread.Stack
            1.New:被創建尚未啓動(未調用start())的線程
            2.Runnable:可運行的狀態(已用start(),等待CPU調度)
            3.Blocked:線程阻塞等待監視器鎖定的線程狀態,處於synchronized同步代碼塊或方法中被阻塞
            4.Waiting:等待線程的線程狀態(不帶超時機制,沒有被喚醒則會一直等待)。下列不帶有超時的方式:                                                         Object.wait\Thread.join\LockSupport.park
            5.Timed Waiting:具有超時時間的等待狀態。下列帶超時時間的方式:Thread.sleep、Object.wait、Thread.join、                                                     LockSupport.parkNanos、LockSupport.parkUntil
            6.Terminatrred:終止狀態。線程正常完成執行或出現異常
 

 線程之間的切換:

 
    


    停止線程:
        不建議:Stop()方法停止線程,該方法立即終止線程,並且清楚監視器鎖的信息,可能導致線程安全問題,JDK不建議使用
        正確的線程中終止:
            1.Interrupt():該方法在目標線程調用休眠或等待方法時,該線程的中斷狀態將被清楚,拋出InterruptedException異常,
                                   如果目標線程是被I/O或者NIO中的channel鎖阻塞,同樣返回特殊異常達到終止線程的目的。如果以上條
                                   件都不滿足,則會設置此線程的中斷狀態。JDK推薦使用標誌位↓
            2.標誌位:boolean值的標誌位控制程序是否執行,例如:while(isRunning){}
    
    線程通信:指線程在運行期間的數據通信。如線程執行的先後順序、獲取某個線程執行的結果等等,分爲四類:
        1.文件共享
        2.網絡共享
        3.共享變量
        4.jdk提供的線程協調API:
                suspend/resume(已廢棄)、wait/notify、park/unpark等
    
    線程協作:JDK提供線程協作的API
        多線程協作的經典場景:生產者 --> 消費者 例:線程A買包子,沒有包子則不再執行 線程B生產包子,通知線程A買包子
        有三種線程協作API:
                1.suspend/resume: suspend掛起目標線程,resume恢復線程執行。掛起線程時不釋放鎖,比較容易寫出死鎖代碼,                                                   不推薦使用,被廢棄
                2.wait/notify:wait使線程進入等待,notify喚醒線程。wait進入等待時釋放鎖,一定程度上避免死鎖但有執行順序
                                       要求:在notify之後調用wait線程會一直處於等待(WAITING)狀態。該方法只能對同一對象鎖的持有                                                   者線程調用,否則拋出illegalMonitorStateException
                3.park/unpark:park使線程等待獲取“許可”狀態,unpark提供“許可”狀態。多次調用unpark之後再park線程會直接
                                         運行,解決了 wait/notify在notify之後調用wait線程會一直處於WAITING的情況,但方法本身不會                                                       釋放鎖,也會出現第一種API的死鎖情況
                    park/unpark機制:聲明一個線程當前狀態位,假設爲0或1,默認爲0。當調用park時,檢查狀態位,爲1則繼續運行                                                 (Runnable狀態)並將狀態位設置爲0,爲0則將線程設置爲waiting狀態並實時監控狀態位。
                                         當調用unpark時,將狀態位設置爲1,當值爲1時不做改變。可參考
                                                 https://blog.csdn.net/shijiejiujiuba/article/details/79034307
        
    線程封閉:通過將數據封閉在線程中而避免使用同步技術稱爲線程封閉(數據被封閉在各自的線程中,不需要同步)
        具體實現:
            1.ThreadLocal:線程變量。例:先成名一個ThreadLocal<T> a = new ThreadLocal<T>();此後創建的每個線程,使用的都                                              是a的副本,副本相互獨立互不影響,修改情況下不影響原先聲明的a
            2.局部變量:局部變量的固有屬性之一就是封閉在線程中。位於執行線程的棧中,其他線程無法訪問這個棧

    線程池:
        爲什麼要使用線程池,線程池是不是越多越好?
            1.線程在java中使一個對象,更是操作系統的資源,線程創建、銷燬需要時間。如果創建時間+銷燬時間>執行任務的時                             間,就很不划算
            2.java對象佔用堆內存,操作系統線程佔用系統內存,根據jvm規範,一個線程默認最大棧大小1mb,這個棧空間是需要                             從系統內從中分配的。線程過多會小號很多內存
            3.操作系統需要頻繁切換線程上下文(大家都想被運行),影響性能
            
        概念:
            1.線程池管理器:用於創建並管理線程,包括創建/銷燬線程池,添加新任務
            2.工作線程: 線程池中線程,在沒有任務的時候處於等待狀態,可以循環的執行任務
            3.任務接口:每個任務必須實現的接口,供工作線程調度任務的執行,主要規定了任務入口,任務執行完後的收尾工                                                    作,任務的執行狀態等
            4.任務隊列:用於存放沒有處理得當任務。提供緩衝機制
            
        API:線程池API,接口定義和實現類
            
--> 內存屏障和CPU緩存
    cpu高速緩存,儘可能的避免處理器訪問主內存的時間開銷,處理器大多會利用緩存提高性能
    
    cpu多級緩存:
        l1:以及緩存是cpu第一層高速緩存,分爲數據緩存和指令緩存。一般服務器cpu的l1緩存容量在32-4096kb
        l2:高速存儲器
        l3:具有較大l3級緩存的處理器提供更有效的文件系統緩存行爲及較短消息和處理器長度。一般是多核共享一個l3級緩存
        
        cpu按照 L1 -> L2 -> L3 -> 內存 -> 外部存儲器 的順序讀取數據
    
    緩存同步協議:
              多cpu讀取同樣的數據進行緩存,進行不同運算之後。最終寫入主內存以哪個cpu爲準?在這種高速緩存回寫的場景下,                          有緩存一致性協議多數cpu廠商對它進行了實現↓
      MESI協議,它規定每條緩存有個狀態位,同時定義了下面四個狀態:
            修改態:此cache已經被修改過(贓行),內存已不同於主存,爲此cache專有
            專有態:此cache行內容同於主存。但不出現與其他cache中
            共享態:此cache行內容同於主存。但也出現於其它cache中
            無效態:此cache行內容無效(空行)
            
        多處理器時,單個cpu對緩存中數據進行了改動,需要通知給其他cpu。也就是意味着,cpu處理又要控制自己的讀寫操作,
        還要箭筒其它cpu發出的通知,從而保證最終一致
        
    cpu性能優化手段---> 運行時指令重排
        指令重排的場景:當CPU寫緩存是發現緩存區塊正被其它cpu佔用,爲了提高cpu處理性能可能將後面的讀緩存命令優先執行
        指令重排需要遵守as-if0serial語義:
            as-if0serial:不管怎麼排序(編譯器和處理器爲了提高並行度),單線程的執行結果不能被改變。編譯器runtinme和處理                                           都必須遵守as-if-serial語義。也就是說:編譯器和處理器不會對存在數據依賴關係的操作做重排序
    
    出現的問題:
        1.CPU高速緩存下:緩存中的數據與主內存的數據並不是實時同步的,各cpu(或核心)間緩存的數據也不是實時同步。在                                                    同一個時間點,各Cpu所看到同一內存地址數據的值可能是不一樣的
        2.CPU指令重排序優化下:雖然遵守了as-if-serial語,單僅在cpu自己執行的情況下能保證結果正確。多核多線程中,指令                                                              邏輯無法分辨因果關聯。可能出現亂序執行導致程序運行結果錯誤(僞喚醒)
        
            

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

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