Java線程同步與異步
線程池
無鎖化的實現方案
分佈鎖的實現方案java
分享的目的:react
進一步掌握多線程編程和應用的技巧,但願對你們在平時的開發中應對高併發編程有所幫助sql
1. 同步相關的方法有數據庫
wait, notify, notifyAll編程
2. 關鍵字多線程
synchronized併發
3. JDK鎖的框架框架
AQS (AbstractQueuedSynchronizer)異步
4. AQS的實現類高併發
java.util.concurrent.locks.ReentrantLock
java.util.concurrent.locks.ReentrantReadWriteLock
java.util.concurrent.CountDownLatch
java.util.concurrent.Semaphore
5. 例子——兩個線程交替打印出100之內的奇數和偶數
主程序:
輸出結果示例:
...
...
...
讀者能夠用兩種其它方法實現,加深本身對Java線程同步和互斥的理解
用 ReentrantLock?
仍是用wait和notify ?
做用:
控制線程併發數量,通常用在控制單機併發度上, 也是實現流控的一種方案;
實現原理:
1. 參數含義
corePoolSize: 核心線程的數量, 在CPU密集型和IO密集型的任務中,這個參數的設置不太同樣:
在CPU密集型的應用中:
一般這個參數被設置爲: 機器cpu核數-1, 例如機器有4個核,這個參數就被設置爲3, 這樣作的即兼顧了最大的併發度,又兼顧了其它非重要的核心任務的執行;
在IO密集的任務中:
一般這個參數被設置爲機器cpu核數*(1.5 - 3),具體狀況還須要根據實際業務狀況進行壓測比較,而後再給出最優的值;
maximumPoolSize: 最大核心線程的數量
poolSize: 當前線程的數量
當用戶向線程池中新提交一個線程的時候,會有以下狀況:
狀況1.
若是當前線程池中線程的數量小於corePoolSize, 就會建立一個新的線程, 並添加到線程池中;
狀況2.
若是當前線程池中線程的數量等於corePoolSize, 而且等待隊列中尚未滿,則把當前用戶添加的線程對象放在等待隊列中;
狀況3.
若是當前線程池中線程的數量大於等於corePoolSize而且小於maximunPoolSize,而且等待隊列已經滿,則建立一個新的線程,並添加到線程池中;
狀況4.
若是當前線程池中線程的數量等於maximunPoolSize, 則會根據線程建立線程時候的拒絕策略,進行相應的處理;
2. java線程對象中run方法和start方法的區別:
2.1 線程對象直接調用run方法,JVM是不會有感知,是不會直接產生一個新的線程, 此時程序運行的方式依然是串行的;
2.2 線程對象直接調用start方法,JVM纔會有感知,會產生一個新的線程, 此時纔會產生併發多線程;
線程池正是充分利用了run方法和start的區別來實現線程的複用;
3. 線程池的核心代碼
下面均是以jdk1.6的線程池的源碼,jdk1.7和jdk1.8線程池實如今上有些變化,但核心思想不變,有興趣能夠本身去研究
提交線程的核心代碼:
執行用戶任務的核心代碼:
用線程池的方案
1. netty的reactor線程模型,參考netty官方或網上相關的資料
2. 異地機房數據庫之間的數據同步:
用表名+主鍵名作hash ,hash值相同的記錄被寫到同一個Kafka的Partition中去,假設一個Partition用一個線程進行消費, 這樣不一樣線程之間寫入目標數據庫的時候,就不會存在數據庫行鎖的競爭關係,間接實現了無鎖化的操做, 即線程之間並行,線程內部串行, 以下圖所示;
1. JDK中各類類型值的原子操做
AtomicInteger
AtomicLong
AtomicBoolean
2. jdk中各類鎖的實現, 本質也是volitate變量+CAS
java.util.concurrent.locks.ReentrantLock
java.util.concurrent.Semaphore
java.util.concurrent.CountDownLatch
1. tair
incr和decr操做,至關因而樂觀鎖
2. Redis/memcache
setNx命令
3. Zookeeper
充分利用watcher機制,建立臨時結點,誰建立成功,誰就得到當前的鎖
4. 數據庫:利用數據庫的行鎖
// 加鎖SQL
update trade_base set status = 1 where trade_no=「XXX」 and status = 0;
// 解鎖SQL
update trade_base set status = 0 where trade_no=「XXX」 and status = 1;
注意trade_base表上一要有trade_no的列的惟一索引
固然具體用那種分佈鎖,還須要結合業務自身的須要,通常來講,在併發量不是別大,數據庫徹底能夠扛得住的狀況下,用數據庫實現分佈鎖最快,最方便,並且性能的損失也很是地小;
固然如今不少場景下,都是分庫分表,而且加鎖和解鎖分別都隻影響一行,對數據庫來講,加鎖和解鎖的 sql也是很是輕量的sql操做,所以在性能損失上不用過多的擔憂。
本文做者:友德
本文爲雲棲社區原創內容,未經容許不得轉載。