1. 程式人生 > >java高併發實戰(九)——鎖的優化和注意事項

java高併發實戰(九)——鎖的優化和注意事項

由於之前看的容易忘記,因此特記錄下來,以便學習總結與更好理解,該系列博文也是第一次記錄,所有有好多不完善之處請見諒與留言指出,如果有幸大家看到該博文,希望報以參考目的看瀏覽,如有錯誤之處,謝謝大家指出與留言。

這裡只是講解下鎖優化思路以及方法的總結,具體技術深究以後慢慢補充

一、鎖優化的思路和方法

鎖優化是指:在多執行緒的併發中當用到鎖時,儘可能讓效能有所提高。一般併發中用到鎖,就是阻塞的併發,前面講到一般併發級別分為阻塞的和非阻塞的(非阻塞的包含:無障礙的,無等待的,無鎖的等等),一旦用到鎖,就是阻塞的,也就是一般最糟糕的併發,因此鎖優化就是在堵塞的情況下去提高效能;所以所鎖的優化就是讓效能儘可能提高,不管怎麼提高,堵塞的也沒有無鎖的併發底。讓鎖定的障礙降到最低,鎖優化並不是說就能解決鎖堵塞造成的效能問題。這是做不到的。

方法如下:

減少鎖持有時間

減小鎖粒度

鎖分離

鎖粗化

鎖消除

二、減少鎖持有時間

舉例:

public synchronized void syncMethod(){
othercode1();
mutextMethod();
othercode2();
}

使用這個鎖會造成其他執行緒進行等待,因此讓鎖的的持有時間減少和鎖的範圍,鎖的零界點就會降低,其他執行緒就會很快獲取鎖,儘可能減少了衝突時間。

改進優化如下:

public void syncMethod2(){
othercode1();
synchronized(this){
mutextMethod();
}
othercode2();
}

三、減小鎖粒度

 將大物件,拆成小物件,好處是:大大增加並行度,降低鎖競爭(同時偏向鎖,輕量級鎖成功率提高)

 提高偏向鎖,輕量級鎖成功率

 HashMap的同步實現( HashMap他是非執行緒安全的實現)

        – Collections.synchronizedMap(Map m)(多執行緒下使用時:用該synchronizedMap封裝方式先封裝讓他實現執行緒同步的)

        – 返回SynchronizedMap物件 

內部實現如下:就是實現對set與get進行加鎖,進行互斥上同步,不管讀還是寫都會拿到這個互斥物件。他變成很重的物件,不管讀還是寫,都會互斥阻塞,讀堵塞寫,寫堵塞讀,當多個讀和寫時執行緒會一個一個的進來。

public V get(Object key) {
 synchronized (mutex) {return m.get(key);}
 }
public V put(K key, V value) {
 synchronized (mutex) {return m.put(key, value);}
}

 ConcurrentHashMap(高效能的hash表,他就是做了減少鎖粒度的實現,他被拆分好像16個Segment,每個Segment就是一個個小的hashmap.。就是把大的hash表拆成若干個小的hash表。)

    – 若干個Segment :Segment[] segments

    – Segment中維護HashEntry

    – put操作時• 先定位到Segment,鎖定一個Segment,執行put

在減小鎖粒度後, ConcurrentHashMap允許若干個執行緒同時進入

五、鎖分離

就是把讀堵塞寫,寫堵塞讀,讀讀堵塞,寫寫堵塞就可以使用所分離;鎖分離,就是讀寫鎖分離讀不用改變資料,所以所有的讀不會產生堵塞。當寫的時候才去進行堵塞。一般讀情況大於鎖,所以使用讀寫鎖會有所提高系統性能。如下圖

1、 ReadWriteLock : 維護了一對鎖,讀鎖可允許多個讀執行緒併發使用,寫鎖是獨佔的。

      根據功能進行鎖分離

      所有讀寫鎖的實現必須確保寫操作對讀操作的記憶體影響。換句話說,一個獲得了讀鎖的執行緒必須能看到前一個釋放的寫鎖所更新的內容。 
            讀寫鎖比互斥鎖允許對於共享資料更大程度的併發。每次只能有一個寫執行緒,但是同時可以有多個執行緒併發地讀資料。ReadWriteLock適用於讀多寫少的併發情況。 

     讀多寫少的情況,可以提高效能(根據功能模組是進行不同鎖,讀鎖跟讀鎖同時進入情況其實就屬於無等待的併發,因此這種操作就是把堵塞的變成非堵塞的,效能就是有所改變)


2、 讀寫分離思想可以延伸,只要操作互不影響,鎖就可以分離

        – 佇列

        – 連結串列

思想也可以理解為:在forkjioning有所提到,就是任務work的偷竊,當執行緒執行自己的任務,和一個執行緒去盜取別人的任務,他們的任務佇列中的資料他們是從兩個不同的端去拿的,這就是熱點分離基本思想,一個從頭部拿,一個從尾部拿。如下:


頭部和尾部之間的操作是不衝突的,所以可以進行高併發操作,當然當佇列中只有一個數據情況就另當別論你了。

六、鎖粗化

(一)、通常情況下,為了保證多執行緒間的有效併發,會要求每個執行緒持有鎖的時間儘量短,即在使用完公共資源後,應該立即釋放鎖。只有這樣,等待在這個鎖上的其他執行緒才能儘早的獲得資源執行任務。但是,凡事都有一個度,如果對同一個鎖不停的進行請求、同步和釋放,其本身也會消耗系統寶貴的資源,反而不利於效能的優化。

因此可以把很多次請求的鎖拿到一個鎖裡面,但前提是:中間不需要的同步的程式碼塊很很快的執行完。

1.舉例如下:

public void demoMethod(){
synchronized(lock){
//do sth.
}
//做其他不需要的同步的工作,但能很快執行完畢
synchronized(lock){
//do sth.
}
}

改進優化如下:

public void demoMethod(){
//整合成一次鎖請求
synchronized(lock){
//do sth.
//做其他不需要的同步的工作,但能很快執行完畢
}
}

2.舉例如下:

for(int i=0;i<CIRCLE;i++){
synchronized(lock){
}
}

該進入下:

synchronized(lock){
for(int i=0;i<CIRCLE;i++){
}
}

七、鎖消除

在即時編譯器時,如果發現不可能被共享的物件,則可以消除這些物件的鎖操作。

有時候對完全不可能加鎖的程式碼執行了鎖操作,因為些鎖並不是我們加的,是JDK的類引用進來的,當我們使用的時候,會自動引進來,所以我們會在不可能出現在多執行緒需要同步的情況就執行了鎖操作。在某些條件成熟下,系統會消除這些鎖。如下:

public static void main(String args[]) throws InterruptedException {
long start = System.currentTimeMillis();
for (int i = 0; i < CIRCLE; i++) {
craeteStringBuffer("JVM", "Diagnosis");
}
    long bufferCost = System.currentTimeMillis() - start;
    System.out.println("craeteStringBuffer: " + bufferCost + " ms");
}
public static String craeteStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();  //他就是實現的多執行緒同步功能
sb.append(s1);  這兩個就是同步操作
sb.append(s2);
return sb.toString();
}
sb是執行緒安全的。但事實上sb他在棧空間引用的,他是區域性變數,他就是線上程內部才會有的,在區域性變量表中,
只有一個執行緒可以執行他,其他執行緒是不可靠,能訪問到他的因此對sb進行所有同步操作都是無意義的。

因此對些情況,虛擬機器提供了一些優化,就是如下操作,虛擬機器開啟server模式

同時進行開啟逃逸分析DoEscapeAnalysis,如果沒有逃逸的就把鎖去掉(EliminateLocks)。逃逸分析是指:看sb是否有可能逃出StringBuffer的作用域。變成sb公有的,全域性的變數,變成其他執行緒可訪問的了。


進行逃逸分析的執行時間,(同時加上去掉鎖操作),


進行逃逸分析的執行時間,(沒有加上去掉鎖操作)。

server模式用法簡單講解:

與client模式相比,server模式的啟動比較慢,因為server模式會嘗試收集更多的系統性能資訊,使用更復雜的優化演算法對程式進行優化。因此當系統完全啟動並進入執行穩定期後,server模式的執行速度會遠遠快於client模式,所以在對於後臺長期執行的系統,使用-server引數啟動對系統的整體效能可以有不小的幫助,但對於使用者介面程式,執行時間不長,又追求啟動速度建議使用-client模式啟動。

未來發展64位系統必然取代32位系統,而64位系統中的虛擬機器更傾向於server模式。

八、虛擬機器內的鎖優化

 偏向鎖

 輕量級鎖

 自旋鎖

     Mark Word,物件頭的標記,32位 (物件頭部儲存一些物件的一些資訊,32位是指系統的位數)

     描述物件的hash、鎖資訊,垃圾回收標記,年齡

        – 指向鎖記錄的指標

        – 指向monitor的指標

        – GC標記

        – 偏向鎖執行緒ID

2、偏向鎖(偏心,就是偏向當前佔有鎖的執行緒,他的思想是悲觀的思想,一般我們都是杞人憂天的,大多情況是沒有競爭的,就可以使用偏向鎖,可以對一個執行緒操作提高效能)

思想:那麼只需要在鎖第一次被擁有的時候,記錄下偏向執行緒ID。這樣偏向執行緒就一直持有著鎖,直到競爭發生才釋放鎖。以後每次同步,檢查鎖的偏向執行緒ID與當前執行緒ID是否一致,如果一致直接進入同步,退出同步也,無需每次加鎖解鎖都去CAS更新物件頭,如果不一致意味著發生了競爭,鎖已經不是總是偏向於同一個執行緒了,這時候需要鎖膨脹為輕量級鎖,才能保證執行緒間公平競爭鎖。

在沒有實際競爭的情況下,還能夠針對部分場景繼續優化。如果不僅僅沒有實際競爭,自始至終,使用鎖的執行緒都只有一個,那麼,維護輕量級鎖都是浪費的。偏向鎖的目標是,減少無競爭且只有一個執行緒使用鎖的情況下,使用輕量級鎖產生的效能消耗。輕量級鎖每次申請、釋放鎖都至少需要一次CAS,但偏向鎖只有初始化時需要一次CAS。

“偏向”的意思是,偏向鎖假定將來只有第一個申請鎖的執行緒會使用鎖(不會有任何執行緒再來申請鎖),因此,只需要在Mark Word中CAS記錄owner(本質上也是更新,但初始值為空),如果記錄成功,則偏向鎖獲取成功,記錄鎖狀態為偏向鎖,以後當前執行緒等於owner就可以零成本的直接獲得鎖;否則,說明有其他執行緒競爭,膨脹為輕量級鎖

偏向鎖無法使用自旋鎖優化,因為一旦有其他執行緒申請鎖,就破壞了偏向鎖的假定。

(1.)大部分情況是沒有競爭的,所以可以通過偏向來提高效能

(2.)所謂的偏向,就是偏心,即鎖會偏向於當前已經佔有鎖的執行緒

(3.)將物件頭Mark的標記設定為偏向,並將執行緒ID寫入物件頭Mark

(4.) 只要沒有競爭,獲得偏向鎖的執行緒,在將來進入同步塊,不需要做同步

(5.)當其他執行緒請求相同的鎖時,偏向模式結束

(6.) -XX:+UseBiasedLocking

        – 預設啟用

(6.) 在競爭激烈的場合,偏向鎖會增加系統負擔(每次偏向模式都會失敗,因為執行緒競爭,就會是偏向鎖結束;所以每一次都很容易結束偏向鎖,就加大了偏向鎖的每一次判斷,偏向鎖就沒有任何效果)

public static List<Integer> numberList =new Vector<Integer>();   //Vector帶有鎖
public static void main(String[] args) throws InterruptedException {
long begin=System.currentTimeMillis();
int count=0;
int startnum=0;
while(count<10000000){
numberList.add(startnum);
startnum+=2;
count++;
}
long end=System.currentTimeMillis();
System.out.println(end-begin);
}

在系統起來時虛擬機器預設啟用偏向時間是4,因為開始的競爭是很激烈的。

3.輕量級鎖(就是如果在偏向鎖失敗時,系統就會有可能去進行輕量級鎖,目的是儘可能不要動用作業系統中層面的互斥,效能差,因為對於作業系統來說,虛擬機器本身就是應用,所以我們在應用層面去解決執行緒同步問題。)

自旋鎖的目標是降低執行緒切換的成本。如果鎖競爭激烈,我們不得不依賴於重量級鎖,讓競爭失敗的執行緒阻塞;如果完全沒有實際的鎖競爭,那麼申請重量級鎖都是浪費的。輕量級鎖的目標是,減少無實際競爭情況下,使用重量級鎖產生的效能消耗,包括系統呼叫引起的核心態與使用者態切換、執行緒阻塞造成的執行緒切換等。

顧名思義,輕量級鎖是相對於重量級鎖而言的。使用輕量級鎖時,不需要申請互斥量,僅僅將Mark Word中的部分位元組CAS更新指向執行緒棧中的Lock Record,如果更新成功,則輕量級鎖獲取成功,記錄鎖狀態為輕量級鎖;否則,說明已經有執行緒獲得了輕量級鎖,目前發生了鎖競爭(不適合繼續使用輕量級鎖),接下來膨脹為重量級鎖

Mark Word是物件頭的一部分;每個執行緒都擁有自己的執行緒棧(虛擬機器棧),記錄執行緒和函式呼叫的基本資訊。二者屬於JVM的基礎內容,此處不做介紹。

當然,由於輕量級鎖天然瞄準不存在鎖競爭的場景,如果存在鎖競爭但不激烈,仍然可以用自旋鎖優化,自旋失敗後再膨脹為重量級鎖

思想就是:判斷執行緒是否持有某個物件鎖,去看他的頭部是否設定了這個物件的mark值,如果有,就說明執行緒擁有了鎖。

 BasicObjectLock

        – 嵌入在執行緒棧中的物件


 普通的鎖處理效能不夠理想,輕量級鎖是一種快速的鎖定方法。

 如果物件沒有被鎖定(判斷步驟)

    – 將物件頭的Mark指標儲存到鎖物件中

    – 將物件頭設定為指向鎖的指標(線上程棧空間中)

如下操作:在虛擬機器層面去進行快速持有鎖與非持有鎖判斷操作,其實就是CAS操作。cas成功,說明你持有鎖,費則則沒有。

lock->set_displaced_header(mark);
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark))
{
 TEVENT (slow_enter: release stacklock) ;
 return ;
}

lock位於執行緒棧中

產生問題:

1. 如果輕量級鎖獲取失敗(CAS失敗),表示存在競爭,升級為重量級鎖(常規鎖)

2. 在沒有鎖競爭的前提下,減少傳統鎖使用OS(作業系統)互斥量產生的效能損耗

3.在競爭激烈時,輕量級鎖會多做很多額外操作,導致效能下降

擴充套件CAS:

CAS:Compare and Swap, 翻譯成比較並交換。 

java.util.concurrent包中藉助CAS實現了區別於synchronouse同步鎖的一種樂觀鎖。

CAS操作包含三個運算元——記憶體位置(V),預期原值(A)和新值(B)。如果記憶體位置的值與預期原值相匹配,那麼處理器將會自動將該位置值更新為新值,否則,不做任何操作。無論哪種情況,它都會在CAS指令之前返回該位置的值。

通過以上定義我們知道CAS其實是有三個步驟的

1.讀取記憶體中的值

2.將讀取的值和預期的值比較

3.如果比較的結果符合預期,則寫入新值

https://blog.csdn.net/liu88010988/article/details/50799978

4.自旋鎖(可以防止在作業系統層面執行緒被掛起)當輕量級鎖沒有拿到失敗時,他就有可能動用作業系統方面的互斥,有可能動用是指,他還可能進行自旋鎖操作。

當競爭存在時,如果執行緒可以很快獲得鎖,那麼可以不在OS層掛起執行緒,讓執行緒做幾個空操作(自旋);當拿不到鎖時,不立即去掛掉執行緒,而是做空迴圈,嘗試再去拿到鎖,當別人釋放鎖時,你就可以拿到鎖。避免執行緒在作業系統層面掛起。避免8萬個時間週期的浪費。

 JDK1.6中-XX:+UseSpinning開啟  1.6可關閉和開啟操作,

 JDK1.7中,去掉此引數,改為內建實現  1.7則把他改為內建開啟

 如果同步塊很長,自旋失敗,會降低系統性能

 如果同步塊很短,自旋成功,節省執行緒掛起切換時間,提升系統性能

因此減少鎖的持有時間也會增加自旋成功率。ConcurrentHashMap就可以使用這個自旋鎖,hashmap的操作是非常快的,所以自旋等待的可能性就會提高。

5.偏向鎖,輕量級鎖,自旋鎖總結(這些都是在虛擬機器層面的優化,不是java層面的方式)

他們不是Java語言層面的鎖優化方法,是虛擬機器層面的方法

內置於JVM中的獲取鎖的優化方法和獲取鎖的步驟

    – 偏向鎖可用會先嚐試偏向鎖

    – 輕量級鎖可用會先嚐試輕量級鎖

    – 以上都失敗,嘗試自旋鎖

    – 再失敗,嘗試普通鎖,使用OS互斥量在作業系統層掛起  OS互斥量:

  (1)、偏向鎖、輕量級鎖、重量級鎖適用於不同的併發場景:

  • 偏向鎖:無實際競爭,且將來只有第一個申請鎖的執行緒會使用鎖。
  • 輕量級鎖:無實際競爭,多個執行緒交替使用鎖;允許短時間的鎖競爭。
  • 重量級鎖:有實際競爭,且鎖競爭時間長。

另外,如果鎖競爭時間短,可以使用自旋鎖進一步優化輕量級鎖、重量級鎖的效能,減少執行緒切換。

如果鎖競爭程度逐漸提高(緩慢),那麼從偏向鎖逐步膨脹到重量鎖,能夠提高系統的整體效能。

6.一個錯誤使用鎖的案例-對不變模式的資料型別進行加鎖操作

public class IntegerLock {
static Integer i=0;  
public static class AddThread extends Thread{
public void run(){
for(int k=0;k<100000;k++){
synchronized(i){
i++;
}
}
}
}
public static void main(String[] args) throws InterruptedException {
AddThread t1=new AddThread();
AddThread t2=new AddThread();
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}

interge 是不變模式的,也就是i值不會發生變化,變化的是i的引用。static Integer i=0;  是不變的,Interge是不可變的,對他i++是不會改變的,因此這裡i++實際的動作是對原始的int做操作,對Interge做++其內部是對他自動拆箱成int進行i++的,這時候改變的不是interge物件的值,而是改變i本身的引用,當i++時,會生成新的Interge,並復到i上,而不是把原來i進行操作,如果每一次都對i做同步,但不同的執行緒操作的i物件可能不是同一個i,第一個可能執行原來的i,下一個執行緒可能執行新的i物件。(可以用上面程式碼測試)

7.ThreadLocal用法案例

ThreadLocal跟鎖是沒有關係,ThreadLocal是最徹底的,可以把鎖完全給替代的東西。

基本思想是:多執行緒中對有資料衝突的物件進行加鎖操作,那麼去掉鎖的簡單方法是,為每一個執行緒都提供一個物件的例項,不同的執行緒訪問自己的物件。

他是執行緒區域性的變數

private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static class ParseDate implements Runnable{
int i=0;
public ParseDate(int i){this.i=i;}
public void run() {
try {
Date t=sdf.parse("2015-03-29 19:29:"+i%60);  //sdf物件他不是執行緒安全的
System.out.println(i+":"+t);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ExecutorService es=Executors.newFixedThreadPool(10);
for(int i=0;i<1000;i++){
es.execute(new ParseDate(i));
}
}

SimpleDateFormat被多執行緒訪問

優化:執行緒安全的

static ThreadLocal<SimpleDateFormat> tl=new ThreadLocal<SimpleDateFormat>();
public static class ParseDate implements Runnable{
int i=0;
public ParseDate(int i){this.i=i;}
public void run() {
try {
if(tl.get()==null){
tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));//每一次要new一個物件
}
Date t=tl.get().parse("2015-03-29 19:29:"+i%60);
System.out.println(i+":"+t);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ExecutorService es=Executors.newFixedThreadPool(10);
for(int i=0;i<1000;i++){
es.execute(new ParseDate(i));
}
}

為每一個執行緒分配一個例項

另外一個錯誤案例:他不會去維護每一個物件的拷貝,實際上tl.get()是把ThreadLocal物件指向同一個物件例項,對所有執行緒來說他還是同一個物件。

static ThreadLocal<SimpleDateFormat> tl=new ThreadLocal<SimpleDateFormat>();
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static class ParseDate implements Runnable{
int i=0;
public ParseDate(int i){this.i=i;}
public void run() {
try {
if(tl.get()==null){
tl.set(sdf);
}
Date t=tl.get().parse("2015-03-29 19:29:"+i%60);//這個還不是執行緒安全的,操作還是同一個執行緒,ThreadLocal指定的還是同一個物件,
System.out.println(i+":"+t);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ExecutorService es=Executors.newFixedThreadPool(10);
for(int i=0;i<1000;i++){
es.execute(new ParseDate(i));
}
}

如果使用共享例項,起不到效果

總結:對於工具等api物件類,資料庫連線例項等希望對每個執行緒持單獨有一個物件,就會減少執行緒的開銷,比如SimpleDateFormat

不需要執行緒之間相互影響,不會產生衝突,就可以使用他。

相關推薦

java併發實戰——優化注意事項

由於之前看的容易忘記,因此特記錄下來,以便學習總結與更好理解,該系列博文也是第一次記錄,所有有好多不完善之處請見諒與留言指出,如果有幸大家看到該博文,希望報以參考目的看瀏覽,如有錯誤之處,謝謝大家指出與留言。這裡只是講解下鎖優化思路以及方法的總結,具體技術深究以後慢慢補充一、

java併發實戰——併發除錯JDK8新特性

由於之前看的容易忘記,因此特記錄下來,以便學習總結與更好理解,該系列博文也是第一次記錄,所有有好多不完善之處請見諒與留言指出,如果有幸大家看到該博文,希望報以參考目的看瀏覽,如有錯誤之處,謝謝大家指出與留言。一、內容提要 多執行緒除錯的方法 執行緒dump及分析 JDK

java併發實戰——BIO、NIOAIO

由於之前看的容易忘記,因此特記錄下來,以便學習總結與更好理解,該系列博文也是第一次記錄,所有有好多不完善之處請見諒與留言指出,如果有幸大家看到該博文,希望報以參考目的看瀏覽,如有錯誤之處,謝謝大家指出與留言。一、什麼是NIO?NIO是New I/O的簡稱,與舊式的基於流的I/

實戰Java併發程式設計四、優化注意事項

在多核時代,使用多執行緒可以明顯地提升系統的效能。但事實上,使用多執行緒會額外增加系統的開銷。對於單任務或單執行緒的應用來說,其主要資源消耗在任務本身。對於多執行緒來說,系統除了處理功能需求外,還需要維護多執行緒環境特有的資訊,如執行緒本身的元資料,執行緒的排程,執行緒上下文的切換等。 4.1有

實戰Java併發程式設計走進併發世界

阻塞(blocking)一個執行緒是阻塞的,那麼其它的執行緒釋放資源之前,當前執行緒無法繼續執行。使用synchronized或者重入鎖會使執行緒這是。 無飢餓(starvation-free):對於非公平的鎖來說,系統允許高優先順序的執行緒插隊,會造成飢餓;而公平的鎖則不會造成飢餓。 無障礙(obstruc

慕課網實戰·併發探索併發容器 J.U.C

特別感謝:慕課網jimin老師的《Java併發程式設計與高併發解決方案》課程,以下知識點多數來自老師的課程內容。 jimin老師課程地址:Java併發程式設計與高併發解決方案 概述 Java併發容器JUC是三個單詞的縮寫。是JDK下面的一個包名。

Java併發程式設計Java併發工具類

1. 等待多執行緒完成的CountDownLatch CountDownLatch允許一個或多個執行緒等待其他執行緒完成操作。 1.1 應用場景 假如有這樣一個需求:我們需要解析一個Excel裡多個sheet的資料,此時可以考慮使用多 執行緒,每個執行緒解析一個sheet裡的資料

Java併發程式設計Java併發容器框架

1. ConcurrentHashMap 1.1 ConcurrentHashMap的優勢 在併發程式設計中使用HashMap可能導致程式死迴圈。而使用執行緒安全的HashTable效率又非 常低下,基於以上兩個原因,便有了ConcurrentHashMap的登場機會。

我所理解的Android模組化——常見問題注意事項

   關於Android模組化,前面已經寫了三篇文章,沒有了解的大家可以先去看一下,附上鍊接地址:   下面主要來說一下Android模組化過程中的常見問題和注意事項: 注意事項   記得在一篇技術部落格中看到微信Tinker的開發人員說過一句話

實戰Java併發程式設計五、並行模式與演算法

5.1單例模式 單例模式:是一種常用的軟體設計模式,在它的核心結構中值包含一個被稱為單例的特殊類。一個類只有一個例項,即一個類只有一個物件例項。  對於系統中的某些類來說,只有一個例項很重要,例如,一個系統中可以存在多個列印任務,但是隻能有一個正在工作的任務;售票時,一共有100張票,可有有

實戰Java併發程式設計3.2 執行緒池

1.Executor jdk提供了一套Executor框架,本質上是一個執行緒池。 newFixedThreadPool()方法:該方法返回一個固定數量的執行緒池。該執行緒池中的執行緒數量始終不變,當有一個新任務提交時,執行緒池中若有空閒執行緒,則立即執行,若沒有,則任務會暫存在一個任

實戰Java併發程式設計3.1同步控制

3.1重入鎖 重入鎖使用java.util.concurrent.locks.ReentrantLock來實現 public class Test implements Runnable { public static ReentrantLock lock = new Reentr

java併發實戰程式設計

總覽:什麼是鎖:鎖就是一種資源,用來進行共享資源的保護操作(也可以認為鎖是一條封鎖線,一個物件獲得了可以通過,否則就只能在隔離帶後面等待,除非輪到自己獲取到許可權)。 一.synchronized功能拓展:重入鎖 ReentrantLock     1.效能和synchr

java併發實戰程式設計

一:Fork/Join 框架:      分而治之的思想:也就是將一項任務分為多個執行過程進行,最後將結果進行整合。在Linux通過fork()函式建立子程序,使得系統程序可以多一個執行分支(執行緒),要等待這個執行分支執行完畢,才有可能得到最終結果,而join()

java併發實戰程式設計

一:倒計時器:countDownLatch       countDownLatch是一個實用的多執行緒控制工具類。又稱為倒計時器,通常用來控制執行緒等待,可以讓某一個執行緒等待直到倒計時結束,再開始執行。       解釋:也就是

Java秒殺實戰 服務級併發秒殺優化RabbitMQ+介面優化

一、思路:減少資料庫訪問 1.系統初始化,把商品庫存數量載入到Redis 2.收到請求,Redis預減庫存,庫存不足,直接返回,否則進入3 3.請求入隊,立即返回排隊中 4.請求出隊,生成訂單,減少庫存 5.客戶端輪詢,是否秒殺成功 二、安裝RabbitMQ及其相

Java併發程式設計ReentrantReadWriteLock

一、ReentrantReadWriteLock簡介 ReentrantReadWriteLock允許同一時間有一個寫執行緒或多個讀執行緒,滿足了對讀寫併發控制有不同需求的場景,相對於排他鎖,提高了併發性。在實際應用中,大部分情況下對共享資料(如快取)的訪問都是讀操作遠多於寫操作,因此JDK提供

C++:併發佇列含雙[隊頭,隊尾]

最近再做一個高併發的伺服器處理程式,伺服器要用多執行緒處理大資料量計算,然後將計算結果封裝成訊息放入佇列中,然後另起幾個執行緒專門負責處理訊息佇列中的訊息分發給不同客戶端,這樣瓶頸就出來了,N多執行緒都在頻繁鎖訊息佇列,這樣導致隊裡的利用效率下降一半,無論是入隊還是出隊都要

併發Java 優化注意事項

1. 鎖優化的思路和方法 在[高併發Java 一] 前言中有提到併發的級別。 一旦用到鎖,就說明這是阻塞式的,所以在併發度上一般來說都會比無鎖的情況低一點。 這裡提到的鎖優化,是指在阻塞式的情況下,如何讓效能不要變得太差。但是再怎麼優化,一般來說效能都會比

Java併發程式設計十二:Executor框架

Java中的執行緒既是工作單元,也是執行單元。工作單元包括Runnable和Callable,而執行單元是由Executor框架支援。 1. Executor框架簡介 1.1 Executor框架的兩級排程模型 在HotSpot VM的執行緒模型中,Java執行緒(java.la