java架構複習整理筆記

2PC 兩階段提交(XA事務,阻塞) 第一個階段: 發出「準備」命令,全部事務參與者接受指令後進行資源準備,鎖準備,undo log準備。若是都返回「準備成功」,若是不能執行,返回終止。 第二個階段 協調者接受到第一個階段的回覆 若是都是ok,則發出「提交」命令,全部參與者進行commit操做。若是都成功,則事務結束,若是有失敗狀況,協調者發出「回滾」命令,全部事務參與者利用undo log進行回滾(這個在2PC不存在)。J2EE對JTA就是兩階段提交的實現。 若是有不ok,則發出撤銷,全部事物撤銷本地資源的鎖定等操做mysql

TCC TCC是對二階段的一個改進,try階段經過預留資源的方式避免了同步阻塞資源的狀況,可是TCC編程須要業務本身實現try,confirm,cancle方法,對業務入侵太大,實現起來也比較複雜。react

分佈式事務的基本原理本質上都是兩階段提交協議(2PC),TCC (try-confirm-cancel)其實也是一種 2PC,只不過 TCC 規定了在服務層面實現的具體細節,即參與分佈式事務的服務方和調用方至少要實現三個方法:try 方法、confirm 方法、cancel 方法。web

Saga Saga模式是現實中可行的方案,採用事務補償機制。每一個本地事務都存儲一個副本,若是出現失敗,則利用補償機制回滾。算法

最終一致性(BASE理論)spring

Paxossql

Raft數據庫

mysql 索引:編程

  1. 普通索引
  2. 惟一索引
  3. 單列、多列索引
  4. 組合索引(最左前綴)

JVM : 垃圾回收機制順序: 1.新生代 Serial (第一代) PraNew (第二代) Parallel Scavenge (第三代) G1收集器(第四代) 2.老年代 Serial Old (第一代) Parallel Old (第二代) CMS (第三代) G1收集器 (第四代)數組

G1以前的JVM內存模型 新生代:伊甸園區(eden space) + 2個倖存區 老年代:持久代(perm space):JDK1.8以前 元空間(metaspace):JDK1.8以後取代持久代緩存

配置參數:

G1 : G1模糊了內存分代概念,可是也保留了年輕代和老年代。因此G1沒有Full GC。Fully young gc和Mixed gc. G1收集器收集範圍是老年代和新生代。不須要結合其餘收集器使用

CMS: CMS收集器是老年代的收集器,能夠配合新生代的Serial和ParNew收集器一塊兒使用

區別一: 使用範圍不同 CMS收集器是老年代的收集器,能夠配合新生代的Serial和ParNew收集器一塊兒使用 G1收集器收集範圍是老年代和新生代。不須要結合其餘收集器使用

區別二: STW的時間 CMS收集器以最小的停頓時間爲目標的收集器。 G1收集器可預測垃圾回收的停頓時間(創建可預測的停頓時間模型)

區別三: 垃圾碎片 CMS收集器是使用「標記-清除」算法進行的垃圾回收,容易產生內存碎片 G1收集器使用的是「標記-整理」算法,進行了空間整合,下降了內存空間碎片。

區別四: 垃圾回收的過程不同 CMS收集器                      G1收集器

  1. 初始標記                   1.初始標記

  2. 併發標記                   2. 併發標記

  3. 從新標記                   3. 最終標記

  4. 併發清楚                   4. 篩選回收

-----------------------------------------------io------------------------------------------------------

bio nio(同步非阻塞) aio(異步非阻塞)

bio: 同步阻塞的實現,代碼以下能夠看到代碼和簡單,這也是bio的一個優點,實現簡單,可是性能是很低的,做爲server端若是有大量連接進來那麼,每一個read write都是阻塞的一個鏈接的請求處理完以前,下一個鏈接就必須等待 nio: aio: Netty不看重Windows上的使用,在Linux系統上,AIO的底層實現仍使用EPOLL,沒有很好實現AIO,所以在性能上沒有明顯的優點,並且被JDK封裝了一層不容易深度優化。 Netty總體架構是reactor模型, 而AIO是proactor模型, 混合在一塊兒會很是混亂,把AIO也改形成reactor模型看起來是把epoll繞個彎又繞回來。 AIO還有個缺點是接收數據須要預先分配緩存, 而不是NIO那種須要接收時才須要分配緩存, 因此對鏈接數量很是大但流量小的狀況, 內存浪費不少。 Linux上AIO不夠成熟,處理回調結果速度跟不上處理需求,好比外賣員太少,顧客太多,供不該求,形成處理速度有瓶頸(待驗證)。

Java NIO知識都在這裏 NIO的特性/NIO與IO區別:

1)IO是面向流的,NIO是面向緩衝區的; 2)IO流是阻塞的,NIO流是不阻塞的; 3)NIO有選擇器,而IO沒有。

讀數據和寫數據方式: 從通道進行數據讀取 :建立一個緩衝區,而後請求通道讀取數據。 從通道進行數據寫入 :建立一個緩衝區,填充數據,並要求通道寫入數據。 NIO核心組件簡單介紹

Channels 一般來講NIO中的全部IO都是從 Channel(通道) 開始的。 NIO Channel通道和流的區別:

Buffers Java NIO Buffers用於和NIO Channel交互。 咱們從Channel中讀取數據到buffers裏,從Buffer把數據寫入到Channels; Buffer本質上就是一塊內存區; 一個Buffer有三個屬性是必須掌握的,分別是:capacity容量、position位置、limit限制。

Selectors Selector 通常稱 爲選擇器 ,固然你也能夠翻譯爲 多路複用器 。它是Java NIO核心組件中的一個,用於檢查一個或多個NIO Channel(通道)的狀態是否處於可讀、可寫。如此能夠實現單線程管理多個channels,也就是能夠管理多個網絡連接。 使用Selector的好處在於: 使用更少的線程來就能夠來處理通道了, 相比使用多個線程,避免了線程上下文切換帶來的開銷。

netty 源碼其實就是用了nio設計,而且使用獨有的reactor

粘包問題的解決策略 因爲底層的TCP沒法理解上層的業務數據,因此在底層是沒法保證數據包不被拆分和重組的,這 個問題只能經過上層的應用協議棧設計來解決。業界的主流協議的解決方案,能夠概括以下:

1.消息定長,報文大小固定長度,例如毎個報文的長度固定爲200字節,若是不夠空位補空格; 2.包尾添加特殊分隔符,例如毎條報文結朿都添加回車換行符(例如FTP協議)或者指定特殊 字符做爲報文分隔符,接收方經過特殊分隔符切分報文區分; 3.將消息分爲消息頭和消息體,消息頭中包含表示信患的總長度(或者消息體長度)的字段; 4.更復雜的自定義應用層協議。

優勢:
減小線程切換的開銷。
複用channel,能夠選擇池化channel EventLoopGroup EventLoop

zero copy的應用 減小併發下的競態狀況

首先來看看Reactor模式,Reactor模式應用於同步I/O的場景。咱們以讀操做爲例來看看Reactor中的具體步驟:

讀取操做:

  1. 應用程序註冊讀就需事件和相關聯的事件處理器
  2. 事件分離器等待事件的發生
  3. 當發生讀就需事件的時候,事件分離器調用第一步註冊的事件處理器
  4. 事件處理器首先執行實際的讀取操做,而後根據讀取到的內容進行進一步的處理

下面咱們來看看Proactor模式中讀取操做和寫入操做的過程:

讀取操做:

  1. 應用程序初始化一個異步讀取操做,而後註冊相應的事件處理器,此時事件處理器不關注讀取就緒事件,而是關注讀取完成事件,這是區別於Reactor的關鍵。
  2. 事件分離器等待讀取操做完成事件
  3. 在事件分離器等待讀取操做完成的時候,操做系統調用內核線程完成讀取操做,並將讀取的內容放入用戶傳遞過來的緩存區中。這也是區別於Reactor的一點,Proactor中,應用程序須要傳遞緩存區。
  4. 事件分離器捕獲到讀取完成事件後,激活應用程序註冊的事件處理器,事件處理器直接從緩存區讀取數據,而不須要進行實際的讀取操做。 Proactor中寫入操做和讀取操做,只不過感興趣的事件是寫入完成事件。

從上面能夠看出,Reactor和Proactor模式的主要區別就是真正的讀取和寫入操做是有誰來完成的,Reactor中須要應用程序本身讀取或者寫入數據,而Proactor模式中,應用程序不須要進行實際的讀寫過程,它只須要從緩存區讀取或者寫入便可,操做系統會讀取緩存區或者寫入緩存區到真正的IO設備.

綜上所述,同步和異步是相對於應用和內核的交互方式而言的,同步須要主動去詢問,而異步的時候內核在IO事件發生的時候通知應用程序,而阻塞和非阻塞僅僅是系統在調用系統調用的時候函數的實現方式而已。

--------------------spring------ aop ioc

Spring的AOP是基於動態代理實現的,Spring會在運行時動態建立代理類,代理類中引用被代理類,在被代理的方法執行先後進行一些神祕的操做。 BTrace基於ASM、Java Attach Api、Instruments開發,爲用戶提供了不少註解。依靠這些註解,咱們能夠編寫BTrace腳本(簡單的Java代碼)達到咱們想要的效果,而沒必要深陷於ASM對字節碼的操做中不可自拔。

----------------------------鎖

synchronized\ReentrantLock\volatile\Atomic(cas)

synchronized : 基於Monitor實現,底層使用操做系統的mutex lock實現的。Class和Object都關聯了一個Monitor。

①.同步實例方法,鎖是當前實例對象 ②.同步類方法,鎖是當前類對象 ③.同步代碼塊,鎖是括號裏面的對象

Monitor;每一個對象都擁有本身的監視器,當這個對象由同步塊或者這個對象的同步方法調用時,執行方法的線程必須先獲取該對象的監視器才能進入同步塊和同步方法,若是沒有獲取到監視器的線程將會被阻塞在同步塊和同步方法的入口處,進入到BLOCKED狀態
複製代碼

synchronized修飾代方法:使用ACC_SYNCHRONIZED標記符隱式的實現

synchronized修飾代碼塊:字節碼裏面 monitorenter(進去) 每個對象都有一個monitor,一個monitor只能被一個線程擁有。當一個線程執行到monitorenter指令時會嘗試獲取相應對象的monitor,獲取規則以下:

若是monitor的進入數爲0,則該線程能夠進入monitor,並將monitor進入數設置爲1,該線程即爲monitor的擁有者。 若是當前線程已經擁有該monitor,只是從新進入,則進入monitor的進入數加1,因此synchronized關鍵字實現的鎖是可重入的鎖。 若是monitor已被其餘線程擁有,則當前線程進入阻塞狀態,直到monitor的進入數爲0,再從新嘗試獲取monitor。

monitorexit(出去) 只有擁有相應對象的monitor的線程才能執行monitorexit指令。每執行一次該指令monitor進入數減1,當進入數爲0時當前線程釋放monitor,此時其餘阻塞的線程將能夠嘗試獲取該monitor。

缺點: 它沒法中斷一個正在等候得到鎖的線程; 也沒法經過投票獲得鎖,若是不想等下去,也就無法獲得鎖

ReentrantLock\讀寫鎖 更加精細,稍微公平、提供了更多選擇(lock,try、try(time)、lockInterruptibly)

性能不一致:資源競爭激勵的狀況下,lock性能會比synchronize好,競爭不激勵的狀況下,synchronize比lock性能好。 鎖機制不同:synchronize是在JVM層面實現的,系統會監控鎖的釋放與否。lock是代碼實現的,須要手動釋放,在finally塊中釋放。能夠採用非阻塞的方式獲取鎖。 用法不同:synchronize能夠用在代碼塊上,方法上。lock經過代碼實現,有更精確的線程語義。


volatile: 在多處理器開發中保證了共享變量的「 可見性」。可見性的意思是當一個線程修改一個共享變量時,另一個線程能讀到這個修改的值。 volatile只保證可見性,不保證原子性

Atomic 原理: JDK經過CPU的cmpxchgl指令的支持,實現AtomicInteger的CAS操做的原子性

CAS :在Java併發應用中一般指CompareAndSwap或CompareAndSet,即比較並交換。

CAS是一個原子操做,它比較一個內存位置的值而且只有相等時修改這個內存位置的值爲新的值,保證了新的值老是基於最新的信息計算的,若是有其餘線程在這期間修改了這個值則CAS失敗。CAS返回是否成功或者內存位置原來的值用於判斷是否CAS成功。

有點: 競爭不大的時候系統開銷小。

問題:

  1. ABA問題 CAS須要在操做值的時候檢查下值有沒有發生變化,若是沒有發生變化則更新,可是若是一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,可是實際上卻變化了。這就是CAS的ABA問題。 常見的解決思路是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那麼A-B-A 就會變成1A-2B-3A。 目前在JDK的atomic包裏提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法做用是首先檢查當前引用是否等於預期引用,而且當前標誌是否等於預期標誌,若是所有相等,則以原子方式將該引用和該標誌的值設置爲給定的更新值。
  2. 循環時間長開銷大 上面咱們說過若是CAS不成功,則會原地自旋,若是長時間自旋會給CPU帶來很是大的執行開銷。
  3. 只能保證一個共享變量的原子操做

-----包 ReenterLock Atomic ConcurrentMap Executors ThreadFactory

locks

-------concurrenthashmap---- jdk1.7 ConcurrentHashMap寫操做只會鎖一段(鎖住Segment中全部元素),對不一樣Segment元素的操做不會互相阻塞,而HashTable用的是synchronized,會鎖住整個對象,至關於一個HashTable上的操做都是並行的,連get方法都會阻塞其餘操做。 換個說法吧,一個HashTable只有一把鎖,最多隻有一個線程獲取到鎖。

jdk1.8

1)Node的val和next均爲volatile型 2)tabAt和casTabAt對應的unsafe操做實現了volatile語義

改進一:取消segments字段,直接採用transient volatile HashEntry<K,V>[] table保存數據,採用table數組元素做爲鎖,從而實現了對每一行數據進行加鎖,進一步減小併發衝突的機率。 改進二:將原先table數組+單向鏈表的數據結構,變動爲table數組+單向鏈表+紅黑樹的結構。對於hash表來講,最核心的能力在於將key hash以後能均勻的分佈在數組中。若是hash以後散列的很均勻,那麼table數組中的每一個隊列長度主要爲0或者1。但實際狀況並不是老是如此理想,雖然ConcurrentHashMap類默認的加載因子爲0.75,可是在數據量過大或者運氣不佳的狀況下,仍是會存在一些隊列長度過長的狀況,若是仍是採用單向列表方式,那麼查詢某個節點的時間複雜度爲O(n);所以,對於個數超過8(默認值)的列表,jdk1.8中採用了紅黑樹的結構,那麼查詢的時間複雜度能夠下降到O(logN),能夠改進性能。

------- classLaoader 有BootStrap , Ext , App和用戶自定義加載器,加載類的時候採用雙親委託機制。特別須要注意的是:加載器ClassLoader的父子關係和繼承關係無關,也和加載器是有哪一個加載器加載的無關,而是在建立加載器時指定的父加載器有關,便是由人工指定的。好比說要肯定加載器A的父加載器,僅僅是由建立對象A時傳進去的父加載器決定,而無論A的類型是什麼,也無論A是由哪一個加載器加載的。

1, 雙親委託機制。 2, 同一個加載器:類A引用到類B,則由類A的加載器去加載類B,保證引用到的類由同一個加載器加載。

ContextClassLoader: Class.forname() DriverManager是JDK的基礎類由BootstrapClassLoader加載,DriverManager加載第三方實現的話,必定會加載BootstrapClassLoader裏面。Java引入了ContextClassLoader。 ContextClassLoader是線程的一個屬性,getter和setter方法

-----二叉樹 紅黑樹: 紅黑樹的特性: (1)每一個節點或者是黑色,或者是紅色。 (2)根節點是黑色。 (3)每一個葉子節點(NIL)是黑色。 [注意:這裏葉子節點,是指爲空(NIL或NULL)的葉子節點!] (4)若是一個節點是紅色的,則它的子節點必須是黑色的。 (5)從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。 有點:

B-樹的特性: 1.關鍵字集合分佈在整顆樹中;

2.任何一個關鍵字出現且只出如今一個結點中;

3.搜索有可能在非葉子結點結束;

4.其搜索性能等價於在關鍵字全集內作一次二分查找;

5.自動層次控制

B+樹(高度低)

1.全部關鍵字都出如今葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字剛好是有序的; 2.不可能在非葉子結點命中; 3.非葉子結點至關因而葉子結點的索引(稀疏索引),葉子結點至關因而存儲關鍵字)數據的數據層; 4.更適合文件索引系統

----threadlocal

它是一個數據結構,有點像HashMap,能夠保存"key : value"鍵值對,可是一個ThreadLocal只能保存一個,而且各個線程的數據互不干擾。

  1. 保存線程上下文信息,在任意須要的地方能夠獲取!!!
  2. 線程安全的,避免某些狀況須要考慮線程安全必須同步帶來的性能損失!!!

threadlocal並不能解決多線程共享變量的問題,同一個 threadlocal所包含的對象,在不一樣的thread中有不一樣的副本,互不干擾 用於存放線程上下文變量,方便同一線程對變量的先後屢次讀取,如事務、數據庫connection鏈接,在web編程中使用的更多 問題: 注意線程池場景使用threadlocal,由於實際變量值存放在了thread的threadlocalmap類型變量中,若是該值沒有remove,也沒有先set的話,可能會獲得之前的舊值 問題: 注意線程池場景下的內存泄露,雖然threadlocal的get/set會清除key(key爲threadlocal的弱引用,value是強引用,致使value不釋放)爲null的entry,可是最好remove。

key 使用強引用:這樣會致使一個問題,引用的 ThreadLocal 的對象被回收了,可是 ThreadLocalMap 還持有 ThreadLocal 的強引用,若是沒有手動刪除,ThreadLocal 不會被回收,則會致使內存泄漏。 key 使用弱引用:這樣的話,引用的 ThreadLocal 的對象被回收了,因爲 ThreadLocalMap 持有 ThreadLocal 的弱引用,即便沒有手動刪除,ThreadLocal 也會被回收。value 在下一次 ThreadLocalMap 調用 set、get、remove 的時候會被清除。 比較以上兩種狀況,咱們能夠發現:因爲 ThreadLocalMap 的生命週期跟 Thread 同樣長,若是都沒有手動刪除對應 key,都會致使內存泄漏,可是使用弱引用能夠多一層保障,弱引用 ThreadLocal 不會內存泄漏,對應的 value 在下一次 ThreadLocalMap 調用 set、get、remove 的時候被清除,算是最優的解決方案。

相關文章
相關標籤/搜索