和之前一篇多線程文章類似,這裏沒有什麼邏輯順序就是一些知識點,也不是很深入,見諒!
1、不是有越多的線程程序運行的越快,因爲在併發編程時,會有更多的上下文切換,死鎖等問題。
2、上下文切換:CPU通過時間片 分配算法來循環執行任務,當前任務執行完一個時間片之後會切換到下一個任務,但是又要保存上一個任務的狀態,以便下一次可以加載到上一狀態,所以任務從保存到在加載的過程叫做上下文切換。
3、如何減少上下文切換?
無鎖併發編程:多線程因爲有所會出現很多競爭,所以儘量避免鎖,EG:將數據的ID按照hash算法取模分段,不同的線程處理不同的段的數據。
CAS算法:Java的Atomic包使用CAS算法來更新數據,不需加鎖。
使用最少線程:避免創建不需要的線程。
協程:在單線程裏實現多任務調度,並在單線程裏維持多個任務間的切換。
4.避免死鎖的辦法:
避免一個線程同時獲得多個鎖
避免一個線程在鎖內同時佔用多個資源,儘量保證每個鎖只佔用一個資源
嘗試使用定時鎖,使用lock.tryLock(timeout)來代替使用內部鎖機制
對於數據庫鎖,加鎖和解鎖必須在一個數據庫連接裏,否則會出現解鎖失敗的情況
Java併發機制的底層實現、
Java代碼編譯後會變成Java字節碼,字節碼被類加載器加載到jvm裏,jvm執行字節碼,最終轉換成彙編指令在CPU上運行,Java所使用的併發 機制依賴jvm的實現和CPU的指令。
volatile
volatile的應用:volatile是輕量級的Synchronized,他保證了共享變量的可見性,(可見性是指:當一個線程修改一個共享變量的值時,另一個線程可以讀取這個修改的值)、恰當使用volatile會比Synchronize效率高,因爲volatile不會有上下文切換和調度。
Volatile的定義:允許線程訪問共享變量,爲了確保共享變量可以一致的更新,應該通過排它鎖單獨的獲得這個變量。
1.Volatile原理:1.Lock前綴指令會引起處理器緩存寫到內存:
2.一個處理器的緩存寫到內存會導致其他處理器的緩存無效。
volatile的優化:jdk7中增加了一個隊列集合類:LinkedTransferQueue 他在使用volatile變量時,通過追加字節的方法來優化隊列的出棧和入棧性能。
那麼他是如何優化的呢?原來緩存行的大小是64字節,所以使用追加的方法填充緩存行,這樣的話就避免了頭結點和尾節點在一行,這樣在修改時也不會相互鎖定。
在兩種情況下不能用這種優化方法:1,當緩存行的大小不是64,2.當沒有對共享變量頻繁的寫
Synchronize
Synchronize也被稱爲重量級鎖,因爲在獲得鎖和釋放鎖都會帶來性能消耗
Synchronize實現鎖有三種表現形式:
1.對於普通的同步方法,鎖的是當前的實例變量
2.對於靜態的同步方法,鎖的是當前類的class對象
3.對於同步同步方法塊,鎖的是{}配置的對象。
鎖的升級與對比:爲了減少鎖申請和釋放帶來的性能消耗,引入了“偏向鎖”“輕量級鎖”。
鎖一共有四種狀態:無鎖狀態,偏向鎖狀態,輕量級鎖狀態,重量級鎖狀態。
鎖可以升級但是不能降級。
偏向鎖:由於大多數情況下鎖其實不具有競爭狀態,所以出現了該鎖,基本的思想是:當一個線程訪問一個同步塊並且獲取鎖時,會在對象頭和幀棧中的鎖記錄裏存儲鎖偏向的線程ID,以後該線程在進入和退出該同步塊時,不需要用CAS來加鎖和解鎖,只需要簡單測試一下對象頭裏的MarkWord裏是否存了指向當前線程的偏向鎖,要是有,表示已經獲得鎖;
偏向鎖的撤銷,使用了一種等到有競爭才釋放的機制,也就是說,當其他線程請求的時候纔會釋放。同時釋放的時候需要等到安全點,即在這個點上沒有正在執行字節碼。偏向鎖是自動開啓的,要實現兩關閉,則通過-XX:BiasedLockingStartupDelay=0來設置。設置之後就默認進入輕量級鎖。
鎖的優缺點:
鎖 |
優點 |
缺點 |
適用場景 |
偏向鎖 |
加鎖和解鎖不需要額外的開銷,和執行費同步方法只有納秒級的差別。 |
若線程之間存在鎖競爭,會帶來額外的鎖撤銷的消耗。 |
適用於只有一個線程訪問同步塊的場景。 |
輕量級鎖 |
競爭的線程不會阻塞,提高了程序的響速度 |
如果始終得不到鎖競爭的線程會陷入自旋,會消耗CPU |
追求響應時間 同步塊執行速度非常快 |
重量級鎖 |
線程競爭不會自旋 |
線程阻塞,響應時間慢 |
追求吞吐量 同步塊執行速度較長 |
原子操作
原子操作是不能被分割的一系列操作。
基於緩存行加鎖和總線加鎖的方式來實現原子操作
緩存鎖鎖掉了內存和CPU之間的通信,開銷比較大,所以現在用的是緩存行加鎖。
但有兩種情況不會使用緩存行鎖:
操作的數據不能被緩存或則一個緩存行不夠存儲
有些處理器不支持緩存鎖定
Java如何實現原子操作:可以通過鎖和循環CAS來實現。