1.JVM調優:堆大小設置,回收器選擇,輔助信息html
2.線程池的實現:java
由核心池和任務隊列組成,先向核心池中提交任務,而後向任務隊列中提交。mysql
當線程池中的核心線程數已滿時,任務就要保存到隊列中了。web
線程池中使用的隊列是 BlockingQueue
接口,經常使用的實現有以下幾種:算法
飽和策略分爲:spring
三、spring bean的六個做用域:singleton、prototype、request、session、application、websocketsql
4.Java垃圾回收機制:數據庫
經過引用計數來判斷一個對象是否能夠被回收。這種方式的特色是實現簡單,並且效率較高,可是它沒法解決循環引用的問題,所以在Java中並無採用這種方式(Python採用的是引用計數法)。編程
在主流的商用程序語言中(Java和C#),都是使用可達性分析算法判斷對象是否存活的。這個算法的基本思路就是經過一系列名爲GC Roots的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的,下圖對象object5, object6, object7雖然有互相判斷,但它們到GC Roots是不可達的,因此它們將會斷定爲是可回收對象。設計模式
JVM內存模型:
名稱 | 特徵 | 做用 | 配置 | 異常 |
---|---|---|---|---|
棧區 | 線程私有,使用一段連續的內存空間 | 存放局部變量表、操做棧、動態連接、方法出口 | -XSs | StackOverflowError OutOfMemoryError |
堆 | 線程共享,生命週期與虛擬機相同 | 保存對象實例 | -Xms -Xmx -Xmn | OutOfMemoryError |
程序計數器 | 線程私有、佔用內存小 | 字節碼行號 | 無 | 無 |
方法區 | 線程共享 | 存儲類加載信息、常量、靜態變量等 | -XX:PermSize -XX:MaxPermSize | OutOfMemoryError |
典型的垃圾回收算法:
【新生代對象】:存放年輕對象的堆空間,年輕對象指剛剛建立,或者經歷垃圾回收次數很少的對象。新生代使用複製和標記-清除垃圾收集算法
【老年代對象】:存放老年對象的堆空間。即爲經歷屢次垃圾回收依然存活的對象。所以在年老代中使用標記-整理垃圾回收算法。
【永久代】:永久代也使用標記-整理算法進行垃圾回收
1.Mark-Sweep(標記-清除)算法
標記清除算法可能產生的最大的問題就是空間碎片
2.Copying(複製)算法
算法思想:將原有的內存空間分爲兩塊相同的存儲空間,每次只使用一塊,在垃圾回收時,將正在使用的內存塊中存活對象複製到未使用的那一塊內存空間中,以後清除正在使用的內存塊中的全部對象,完成垃圾回收。
3.Mark-Compact(標記-壓縮)算法
複製算法的高效性是創建在存活對象少、垃圾對象多的狀況下,這種狀況在新生代比較常見,
可是在老年代中,大部分對象都是存活的對象,若是仍是有複製算法的話,成本會比較高。所以,基於老年代這種特性,應該使用其餘的回收算法。
4.Generational Collection(分代收集)算法
分代算法思想:將內存空間根據對象的特色不一樣進行劃分,選擇合適的垃圾回收算法,以提升垃圾回收的效率。
5.分區算法
算法思想:分區算法將整個堆空間劃分爲連續的不一樣小區間,
每個小區間都獨立使用,獨立回收。
一、強引用
若是一個對象具備強引用,GC毫不會回收它;當內存空間不足,JVM寧願拋出OutOfMemoryError錯誤。通常new出來的對象都是強引用
二、軟引用
若是一個對象具備軟引用,當內存空間不足,GC會回收這些對象的內存,使用軟引用構建敏感數據的緩存。
三、弱引用
若是一個對象具備弱引用,在GC線程掃描內存區域的過程當中,無論當前內存空間足夠與否,都會回收內存,利用jdk中的ThreadLocal就是弱引用的,具體間下面的詳細說明。
四、虛引用
若是一個對象僅持有虛引用,在任什麼時候候均可能被垃圾回收,虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列聯合使用,虛引用主要用來跟蹤對象 被垃圾回收的活動。
加載->連接(驗證+準備+解析)->初始化(使用前的準備)->使用->卸載
類的初始化順序 :(靜態變量、靜態初始化塊)–>(變量、初始化塊)–> 構造器;若是有父類,則順序是:父類static方法 –> 子類static方法 –> 父類構造方法- -> 子類構造方法
是指做爲單個邏輯工做單元執行的一系列操做,要麼徹底地執行,要麼徹底地不執行。 事務處理能夠確保除非事務性單元內的全部操做都成功完成,不然不會永久更新面向數據的資源。經過將一組相關操做組合爲一個要麼所有成功要麼所有失敗的單元,能夠簡化錯誤恢復並使應用程序更加可靠。一個邏輯工做單元要成爲事務,必須知足所謂的ACID(原子性、一致性、隔離性和持久性)屬性。事務是數據庫運行中的一個邏輯工做單位,由DBMS中的事務管理子系統負責事務的處理。
舉個例子加深一下理解:同一個銀行轉帳,A轉1000塊錢給B,這裏存在兩個操做,一個是A帳戶扣款1000元,兩一個操做是B帳戶增長1000元,二者就構成了轉帳這個事務。
最後思考一下,怎麼樣會出現A帳戶扣款1000元,B帳戶金額不變?若是你是把兩個操做放在一個事務裏面,而且是數據庫提供的內在事務支持,那就不會有問題,可是開發人員把兩個操做放在兩個事務裏面,而第二個事務失敗就會出現中間狀態。現實中本身實現的分佈式事務處理不當也會出現中間狀態,這並非事務的錯,事務自己就是規定不會出現中間狀態,是事務實現者作出來的方案有問題。
原子性(Atomic):事務必須是原子工做單元;對於其數據修改,要麼全都執行,要麼全都不執行。一般,與某個事務關聯的操做具備共同的目標,而且是相互依賴的。若是系統只執行這些操做的一個子集,則可能會破壞事務的整體目標。原子性消除了系統處理操做子集的可能性。
一致性(Consistency):事務的一致性指的是在一個事務執行以前和執行以後數據庫都必須處於一致性狀態。這種特性稱爲事務的一致性。假如數據庫的狀態知足全部的完整性約束,就說該數據庫是一致的。
隔離性(Isolation):由併發事務所做的修改必須與任何其它併發事務所做的修改隔離。事務查看數據時數據所處的狀態,究竟是另外一個事務執行以前的狀態仍是中間某個狀態,相互之間存在什麼影響,是能夠經過隔離級別的設置來控制的。
持久性(Durability):事務結束後,事務處理的結果必須可以獲得固化,即寫入數據庫文件中即便機器宕機數據也不會丟失,它對於系統的影響是永久性的。
咱們從另一個方向來講說,若是不對事務進行併發控制,咱們看看數據庫併發操做是會有那些異常情形,有些使咱們能夠接受的,有些是不能接受的,注意這裏的異常就是特定語境下的,並不必定就是錯誤什麼的。假設有一個order表,有個字段叫count,做爲計數用,當前值爲100
第一類丟失更新(Update Lost):此種更新丟失是由於回滾的緣由,因此也叫回滾丟失。此時兩個事務同時更新count,兩個事務都讀取到100,事務一更新成功並提交,count=100+1=101,事務二出於某種緣由更新失敗了,而後回滾,事務二就把count還原爲它一開始讀到的100,此時事務一的更新就這樣丟失了。
髒讀(Dirty Read):此種異常時由於一個事務讀取了另外一個事務修改了可是未提交的數據。舉個例子,事務一更新了count=101,可是沒有提交,事務二此時讀取count,值爲101而不是100,而後事務一出於某種緣由回滾了,而後第二個事務讀取的這個值就是噩夢的開始。
不可重複讀(Not Repeatable Read):此種異常是一個事務對同一行數據執行了兩次或更屢次查詢,可是卻獲得了不一樣的結果,也就是在一個事務裏面你不能重複(即屢次)讀取一行數據,若是你這麼作了,不能保證每次讀取的結果是同樣的,有可能同樣有可能不同。形成這個結果是在兩次查詢之間有別的事務對該行數據作了更新操做。舉個例子,事務一先查詢了count,值爲100,此時事務二更新了count=101,事務一再次讀取count,值就會變成101,兩次讀取結果不同。
第二類丟失更新(Second Update Lost):此種更新丟失是由於更新被其餘事務給覆蓋了,也能夠叫覆蓋丟失。舉個例子,兩個事務同時更新count,都讀取100這個初始值,事務一先更新成功並提交,count=100+1=101,事務二後更新成功並提交,count=100+1=101,因爲事務二count仍是從100開始增長,事務一的更新就這樣丟失了。
幻讀(Phantom Read):幻讀和不可重複讀有點像,只是針對的不是數據的值而是數據的數量。此種異常是一個事務在兩次查詢的過程當中數據的數量不一樣,讓人覺得發生幻覺,幻讀大概就是這麼得來的吧。舉個例子,事務一查詢order表有多少條記錄,事務二新增了一條記錄,而後事務一查了一下order表有多少記錄,發現和第一次不同,這就是幻讀。
看到上面提到的幾種問題,你可能會想,我擦,這麼多坑怎麼辦啊。其實上面幾種狀況並非必定都要避免的,具體看你的業務要求,包括你數據庫的負載都會影響你的決定。不知道你們發現沒有,上面各類異常狀況都是多個事務之間相互影響形成的,這說明兩個事務之間須要某種方式將他們從某種程度上分開,下降直至避免相互影響。這時候數據庫事務隔離級別就粉墨登場了,而數據庫的隔離級別實現通常是經過數據庫鎖實現的。
讀未提交(Read Uncommitted):該隔離級別指即便一個事務的更新語句沒有提交,可是別的事務能夠讀到這個改變,幾種異常狀況均可能出現。極易出錯,沒有安全性可言,基本不會使用。
讀已提交(Read Committed):該隔離級別指一個事務只能看到其餘事務的已經提交的更新,看不到未提交的更新,消除了髒讀和第一類丟失更新,這是大多數數據庫的默認隔離級別,如Oracle,Sqlserver。
可重複讀(Repeatable Read):該隔離級別指一個事務中進行兩次或屢次一樣的對於數據內容的查詢,獲得的結果是同樣的,但不保證對於數據條數的查詢是同樣的,只要存在讀改行數據就禁止寫,消除了不可重複讀和第二類更新丟失,這是Mysql數據庫的默認隔離級別。
串行化(Serializable):意思是說這個事務執行的時候不容許別的事務併發執行.徹底串行化的讀,只要存在讀就禁止寫,但能夠同時讀,消除了幻讀。這是事務隔離的最高級別,雖然最安全最省心,可是效率過低,通常不會用。
級別\異常 | 第一類更新丟失 | 髒讀 | 不可重複讀 | 第二類丟失更新 | 幻讀 |
---|---|---|---|---|---|
讀未提交 | Y | Y | Y | Y | Y |
讀已提交 | N | N | Y | Y | Y |
可重複讀 | N | N | N | N | Y |
串行化 | N | N | N | N | N |
通常能夠分爲兩類,一個是悲觀鎖,一個是樂觀鎖,悲觀鎖通常就是咱們一般說的數據庫鎖機制,樂觀鎖通常是指用戶本身實現的一種鎖機制,好比hibernate實現的樂觀鎖甚至編程語言也有樂觀鎖的思想的應用。
悲觀鎖:顧名思義,就是很悲觀,它對於數據被外界修改持保守態度,認爲數據隨時會修改,因此整個數據處理中須要將數據加鎖。悲觀鎖通常都是依靠關係數據庫提供的鎖機制,事實上關係數據庫中的行鎖,表鎖不管是讀寫鎖都是悲觀鎖。
共享鎖(Share locks簡記爲S鎖):也稱讀鎖,事務A對對象T加s鎖,其餘事務也只能對T加S,多個事務能夠同時讀,但不能有寫操做,直到A釋放S鎖。
排它鎖(Exclusivelocks簡記爲X鎖):也稱寫鎖,事務A對對象T加X鎖之後,其餘事務不能對T加任何鎖,只有事務A能夠讀寫對象T直到A釋放X鎖。
更新鎖(簡記爲U鎖):用來預約要對此對象施加X鎖,它容許其餘事務讀,但不容許再施加U鎖或X鎖;當被讀取的對象將要被更新時,則升級爲X鎖,主要是用來防止死鎖的。由於使用共享鎖時,修改數據的操做分爲兩步,首先得到一個共享鎖,讀取數據,而後將共享鎖升級爲排它鎖,而後再執行修改操做。這樣若是同時有兩個或多個事務同時對一個對象申請了共享鎖,在修改數據的時候,這些事務都要將共享鎖升級爲排它鎖。這些事務都不會釋放共享鎖而是一直等待對方釋放,這樣就形成了死鎖。若是一個數據在修改前直接申請更新鎖,在數據修改的時候再升級爲排它鎖,就能夠避免死鎖。
樂觀鎖:顧名思義,就是很樂觀,每次本身操做數據的時候認爲沒有人回來修改它,因此不去加鎖,可是在更新的時候會去判斷在此期間數據有沒有被修改,須要用戶本身去實現。既然都有數據庫提供的悲觀鎖能夠方便使用爲何要使用樂觀鎖呢?對於讀操做遠多於寫操做的時候,大多數都是讀取,這時候一個更新操做加鎖會阻塞全部讀取,下降了吞吐量。最後還要釋放鎖,鎖是須要一些開銷的,咱們只要想辦法解決極少許的更新操做的同步問題。換句話說,若是是讀寫比例差距不是很是大或者你的系統沒有響應不及時,吞吐量瓶頸問題,那就不要去使用樂觀鎖,它增長了複雜度,也帶來了額外的風險。
全部字段:和待更新字段相似,只是使用全部字段作版本控制信息,只有全部字段都沒變化纔會執行更新。
新系統設計可使用version方式和timestamp方式,須要增長字段,應用範圍是整條數據,不論那個字段修改都會更新version,也就是說兩個事務更新同一條記錄的兩個不相關字段也是互斥的,不能同步進行。舊系統不能修改數據庫表結構的時候使用數據字段做爲版本控制信息,不須要新增字段,待更新字段方式只要其餘事務修改的字段和當前事務修改的字段沒有重疊就能夠同步進行,併發性更高。
實踐是檢驗真理的惟一標準,掌握上面的理論以後,咱們在數據庫上實戰一番家裏更好地掌握也加深理解,同時有助於解決實際問題。不一樣數據庫不少實現可能不一樣,這裏以mysql爲例講解各類隔離級別下的狀況,測試表爲user(id,name,gender,passwd,email)。
髒讀測試流程:
1. A設置隔離級別爲read-uncommitted(注意這裏未聲明都是session級別,而非全局的),開啓事務,查詢id=1的記錄
2. B設置隔離級別爲read-uncommitted,開啓事務,修改id=1的記錄,但不提交
3. A再次查詢id=1的記錄,和第一次查詢的比較一下
4. B事務回滾,A事務回滾。
A:
B:
結論:A讀到了B沒有提交的內容,隔離級別爲read-uncommitted的時候出現髒讀。
第一類更新丟失測試流程:
1. A設置隔離級別爲read-uncommitted,開啓事務,查詢id=1的記錄
2. B設置隔離級別爲read-uncommitted,開啓事務,查詢id=1的記錄
3. A修改id=1的記錄
4. B修改id=1的記錄
5. A提交
6. B回滾
7. A在查詢一次id=1的記錄,看看本身的修改是否成功
結論:結果不如我所想的,A的更新成功了,爲何呢?A執行update語句的時候對該條記錄加鎖了,B這時候根本沒法修改直至超時,也就是至少在mysql中在read-uncommitted隔離級別下驗證第一類丟失更新,據瞭解有的數據庫好像能夠設置不加鎖,若是可以不加鎖的話則能夠實現,也貼一下圖吧。
A:
B:
不可重複讀測試流程(省略):
結論:流程和測試髒讀同樣,其實在第一次測試髒讀的時候就能夠發現會出現不可重複讀,A兩次讀取id=1的數據內容不一樣。
第二類丟失更新流程:
1. A開啓事務,查詢order_id=1的記錄
2. B開啓事務,查詢order_id=1的記錄
3. A把查出來的count加1後更新
4. B把查出來的count加1更新
5. A提交,B也提交
A:
B:
結論:A的更新丟失,咱們但願的結果是3,而實際結果是2,跟java的多線程很像對不對,read-uncommitted隔離模式下會出現第二類丟失更新。
幻讀測試流程:
1. A開啓事務,查詢user表全部數據
2. B開啓事務,新增一條記錄
3. A再次查詢user表全部記錄,和第一次做比對
4. A回滾,B回滾
A:
B:
結論:A兩次查詢全表數據結果不一樣,read-uncommitted隔離模式下會出現幻讀。
注:由於後面對這幾種異常狀況的測試流程基本和上面同樣,個別有些差異讀者本身注意,另外注意更改隔離級別便可,就能看到對應結果,後面的我只給出進一步能解決的異常測試截圖,結論能夠參照前面的對照表。
髒讀測試截圖
A:
B:
結論:A沒有讀到B沒有提交的內容,隔離級別爲read-committed的時候不會出現髒讀。
不可重複讀測試截圖
A:
B:
結論:A兩次讀取id=1的數據內容相同,repeatable-read隔離模式下不會出現不可重複讀。
幻讀測試截圖
A:
B:
.
結論:由於A事務未提交以前,B事務插入操做沒法得到鎖而超時,Serializable隔離模式下不會出現幻讀
spring AOP
一 AOP的基本概念
(1)Aspect(切面):一般是一個類,裏面能夠定義切入點和通知
(2)JointPoint(鏈接點):程序執行過程當中明確的點,通常是方法的調用
(3)Advice(通知):AOP在特定的切入點上執行的加強處理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入點):就是帶有通知的鏈接點,在程序中主要體現爲書寫切入點表達式
(5)AOP代理:AOP框架建立的對象,代理就是目標對象的增強。Spring中的AOP代理可使JDK動態代理,也能夠是CGLIB代理,前者基於接口,後者基於子類
動態代理
spring事務傳播特性。
傳播行爲 |
含義 |
PROPAGATION_REQUIRED(XML文件中爲REQUIRED) |
表示當前方法必須在一個具備事務的上下文中運行,若有客戶端有事務在進行,那麼被調用端將在該事務中運行,不然的話從新開啓一個事務。(若是被調用端發生異常,那麼調用端和被調用端事務都將回滾) |
PROPAGATION_SUPPORTS(XML文件中爲SUPPORTS) |
表示當前方法沒必要須要具備一個事務上下文,可是若是有一個事務的話,它也能夠在這個事務中運行 |
PROPAGATION_MANDATORY(XML文件中爲MANDATORY) |
表示當前方法必須在一個事務中運行,若是沒有事務,將拋出異常 |
PROPAGATION_NESTED(XML文件中爲NESTED) |
表示若是當前方法正有一個事務在運行中,則該方法應該運行在一個嵌套事務中,被嵌套的事務能夠獨立於被封裝的事務中進行提交或者回滾。若是封裝事務存在,而且外層事務拋出異常回滾,那麼內層事務必須回滾,反之,內層事務並不影響外層事務。若是封裝事務不存在,則同PROPAGATION_REQUIRED的同樣 |
PROPAGATION_NEVER(XML文件中爲NEVER) |
表示當方法務不該該在一個事務中運行,若是存在一個事務,則拋出異常 |
PROPAGATION_REQUIRES_NEW(XML文件中爲REQUIRES_NEW) |
表示當前方法必須運行在它本身的事務中。一個新的事務將啓動,並且若是有一個現有的事務在運行的話,則這個方法將在運行期被掛起,直到新的事務提交或者回滾才恢復執行。 |
PROPAGATION_NOT_SUPPORTED(XML文件中爲NOT_SUPPORTED) |
表示該方法不該該在一個事務中運行。若是有一個事務正在運行,他將在運行期被掛起,直到這個事務提交或者回滾才恢復執行 |