高并发下Java多线程编程基础
摘要:?Java線程同步與異步 線程池 無鎖化的實現方案 分布鎖的實現方案 分享的目的: 進一步掌握多線程編程和應用的技巧,希望對大家在平時的開發中應對高并發編程有所幫助 Java線程同步與異步 1. 同步相關的方法有 wait, notify, notifyAll 2.
Java線程同步與異步
線程池
無鎖化的實現方案
分布鎖的實現方案
分享的目的:
進一步掌握多線程編程和應用的技巧,希望對大家在平時的開發中應對高并發編程有所幫助
Java線程同步與異步
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用一個線程進行消費, 這樣不同線程之間寫入目標數據庫的時候,就不會存在數據庫行鎖的競爭關系,間接實現了無鎖化的操作, 即線程之間并行,線程內部串行, 如下圖所示;
用CAS的命令
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操作,因此在性能損失上不用過多的擔心;
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的高并发下Java多线程编程基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 理论与实践:如何从Hadoop迁移到Ma
- 下一篇: Java小白进阶笔记(3)-初级面向对象