本文目錄一覽:
Java並行編程有什麼書推薦?
《Java並發編程的藝術》(方騰飛)電子書網盤下載免費在線閱讀
資源鏈接:
鏈接:
提取碼: 37t6
書名:Java並發編程的藝術
作者:方騰飛
豆瓣評分:7.4
出版社:機械工業出版社
出版年份:2015-7-1
頁數:240
內容簡介:
並發編程領域的扛鼎之作,作者是阿里和1號店的資深Java技術專家,對並發編程有非常深入的研究,《Java並發編程的藝術》是他們多年一線開發經驗的結晶。本書的部分內容在出版早期發表在Java並發編程網和InfoQ等技術社區,得到了非常高的評價。它選取了Java並發編程中最核心的技術進行講解,從JDK源碼、JVM、CPU等多角度全面剖析和講解了Java並發編程的框架、工具、原理和方法,對Java並發編程進行了最為深入和透徹的闡述。
《Java並發編程的藝術》內容涵蓋Java並發編程機制的底層實現原理、Java內存模型、Java並發編程基礎、Java中的鎖、並發容器和框架、原子類、並發工具類、線程池、Executor框架等主題,每個主題都做了深入的講解,同時通過實例介紹了如何應用這些技術。
作者簡介:
方騰飛(花名清英,英文名kiral),
螞蟻金服集團技術專家,從事Java開發近10年。5年以上的團隊管理、項目管理和敏捷開發經驗,崇尚團隊合作。曾參與CMS、電子海圖、SOC、ITIL、電子商務網站和信貸管理系統等項目。目前在螞蟻金服網商銀行貸款管理團隊負責數據採集平台開發工作。與同事合作開發了tala code Review插件,深受阿里數千名工程師擁躉,並開發過開源工具jdbcutil()。創辦了並發編程網,組織翻譯了百餘篇國外優秀技術文章,並曾為InfoQ撰寫“聊聊並發”專欄,在《程序員》雜誌撰寫敏捷實踐系列文章
魏 鵬,
阿里巴巴集團技術專家,在阿里巴巴中國網站技術部工作多年,曾擔任中國網站交易平台架構師,主導了交易系統服務化工作,設計實現的數據遷移系統高效地完成了阿里巴巴中國網站交易數據到阿里巴巴集團的遷移工作。目前在阿里巴巴共享業務事業部從事Java應用容器Pandora和服務框架HSF的相關工作,其中Java應用容器Pandora是阿里巴巴中間件運行的基礎,而服務框架HSF則是阿里巴巴集團實現服務化的主要解決方案,二者在阿里巴巴擁有最為廣泛的使用量。個人平時喜歡閱讀技術書籍,翻譯一些國外優秀文檔,喜歡總結、樂於分享,對Java應用容器、多線程編程以及分布式系統感興趣。
程曉明,
1號店資深架構師,從事1號店交易平台系統的開發,技術上關注並發與NIO。因5年前遇到的一個線上故障,解決過程中對Java並發編程產生了濃厚的興趣,從此開始了漫長的探索之旅:從底層實現機制、內存模型到Java同步。縱觀我自己對Java並發的學習過程,是一個從高層到底層再到高層的一個反覆迭代的過程,我估計很多讀者的學習過程應該與我類似。文章多見諸《IBM developerWorks》、InfoQ和《程序員》雜誌。
java並發包源碼怎麼讀
1. 各種同步控制工具的使用
1.1 ReentrantLock
ReentrantLock感覺上是synchronized的增強版,synchronized的特點是使用簡單,一切交給JVM去處理,但是功能上是比較薄弱的。在JDK1.5之前,ReentrantLock的性能要好於synchronized,由於對JVM進行了優化,現在的JDK版本中,兩者性能是不相上下的。如果是簡單的實現,不要刻意去使用ReentrantLock。
相比於synchronized,ReentrantLock在功能上更加豐富,它具有可重入、可中斷、可限時、公平鎖等特點。
首先我們通過一個例子來說明ReentrantLock最初步的用法:
package test;
import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0;
@Override public void run() { for (int j = 0; j 10000000; j++)
{ lock.lock(); try
{
i++;
} finally
{ lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
有兩個線程都對i進行++操作,為了保證線程安全,使用了 ReentrantLock,從用法上可以看出,與 synchronized相比,ReentrantLock就稍微複雜一點。因為必須在finally中進行解鎖操作,如果不在 finally解鎖,有可能代碼出現異常鎖沒被釋放,而synchronized是由JVM來釋放鎖。
那麼ReentrantLock到底有哪些優秀的特點呢?
1.1.1 可重入
單線程可以重複進入,但要重複退出
lock.lock();
lock.lock();try{
i++;
}
finally{
lock.unlock();
lock.unlock();
}
由於ReentrantLock是重入鎖,所以可以反覆得到相同的一把鎖,它有一個與鎖相關的獲取計數器,如果擁有鎖的某個線程再次得到鎖,那麼獲取計數器就加1,然後鎖需要被釋放兩次才能獲得真正釋放(重入鎖)。這模仿了 synchronized 的語義;如果線程進入由線程已經擁有的監控器保護的 synchronized 塊,就允許線程繼續進行,當線程退出第二個(或者後續) synchronized 塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個synchronized 塊時,才釋放鎖。
public class Child extends Father implements Runnable{ final static Child child = new Child();//為了保證鎖唯一
public static void main(String[] args) { for (int i = 0; i 50; i++) { new Thread(child).start();
}
}
public synchronized void doSomething() {
System.out.println(“1child.doSomething()”);
doAnotherThing(); // 調用自己類中其他的synchronized方法
}
private synchronized void doAnotherThing() { super.doSomething(); // 調用父類的synchronized方法
System.out.println(“3child.doAnotherThing()”);
}
@Override
public void run() {
child.doSomething();
}
}class Father { public synchronized void doSomething() {
System.out.println(“2father.doSomething()”);
}
}
我們可以看到一個線程進入不同的 synchronized方法,是不會釋放之前得到的鎖的。所以輸出還是順序輸出。所以synchronized也是重入鎖
輸出:
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
…
1.1.2.可中斷
與synchronized不同的是,ReentrantLock對中斷是有響應的。中斷相關知識查看[高並發Java 二] 多線程基礎
普通的lock.lock()是不能響應中斷的,lock.lockInterruptibly()能夠響應中斷。
我們模擬出一個死鎖現場,然後用中斷來處理死鎖
package test;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock; public Test(int lock)
{ this.lock = lock;
} @Override
public void run()
{ try
{ if (lock == 1)
{
lock1.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock2.lockInterruptibly();
} else
{
lock2.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock1.lockInterruptibly();
}
} catch (Exception e)
{ // TODO: handle exception
} finally
{ if (lock1.isHeldByCurrentThread())
{
lock1.unlock();
} if (lock2.isHeldByCurrentThread())
{
lock2.unlock();
}
System.out.println(Thread.currentThread().getId() + “:線程退出”);
}
} public static void main(String[] args) throws InterruptedException {
Test t1 = new Test(1);
Test t2 = new Test(2);
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
Thread.sleep(1000); //DeadlockChecker.check();
} static class DeadlockChecker
{ private final static ThreadMXBean mbean = ManagementFactory
.getThreadMXBean(); final static Runnable deadlockChecker = new Runnable()
{ @Override
public void run()
{ // TODO Auto-generated method stub
while (true)
{ long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); if (deadlockedThreadIds != null)
{
ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds); for (Thread t : Thread.getAllStackTraces().keySet())
{ for (int i = 0; i threadInfos.length; i++)
{ if(t.getId() == threadInfos[i].getThreadId())
{
t.interrupt();
}
}
}
} try
{
Thread.sleep(5000);
} catch (Exception e)
{ // TODO: handle exception
}
}
}
};
public static void check()
{
Thread t = new Thread(deadlockChecker);
t.setDaemon(true);
t.start();
}
}
}
上述代碼有可能會發生死鎖,線程1得到lock1,線程2得到lock2,然後彼此又想獲得對方的鎖。
我們用jstack查看運行上述代碼後的情況
的確發現了一個死鎖。
DeadlockChecker.check();方法用來檢測死鎖,然後把死鎖的線程中斷。中斷後,線程正常退出。
1.1.3.可限時
超時不能獲得鎖,就返回false,不會永久等待構成死鎖
使用lock.tryLock(long timeout, TimeUnit unit)來實現可限時鎖,參數為時間和單位。
舉個例子來說明下可限時:
package test;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); @Override
public void run()
{ try
{ if (lock.tryLock(5, TimeUnit.SECONDS))
{
Thread.sleep(6000);
} else
{
System.out.println(“get lock failed”);
}
} catch (Exception e)
{
} finally
{ if (lock.isHeldByCurrentThread())
{
lock.unlock();
}
}
}
public static void main(String[] args)
{
Test t = new Test();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();
}
}
使用兩個線程來爭奪一把鎖,當某個線程獲得鎖後,sleep6秒,每個線程都只嘗試5秒去獲得鎖。
所以必定有一個線程無法獲得鎖。無法獲得後就直接退出了。
輸出:
get lock failed
1.1.4.公平鎖
使用方式:
public ReentrantLock(boolean fair) public static ReentrantLock fairLock = new ReentrantLock(true);
一般意義上的鎖是不公平的,不一定先來的線程能先得到鎖,後來的線程就後得到鎖。不公平的鎖可能會產生飢餓現象。
公平鎖的意思就是,這個鎖能保證線程是先來的先得到鎖。雖然公平鎖不會產生飢餓現象,但是公平鎖的性能會比非公平鎖差很多。
1.2 Condition
Condition與ReentrantLock的關係就類似於synchronized與Object.wait()/signal()
await()方法會使當前線程等待,同時釋放當前鎖,當其他線程中使用signal()時或者signalAll()方法時,線 程會重新獲得鎖並繼續執行。或者當線程被中斷時,也能跳出等待。這和Object.wait()方法很相似。
awaitUninterruptibly()方法與await()方法基本相同,但是它並不會再等待過程中響應中斷。 singal()方法用於喚醒一個在等待中的線程。相對的singalAll()方法會喚醒所有在等待中的線程。這和Obejct.notify()方法很類似。
這裡就不再詳細介紹了。舉個例子來說明:
package test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static Condition condition = lock.newCondition();
@Override public void run() { try
{ lock.lock();
condition.await();
System.out.println(“Thread is going on”);
} catch (Exception e)
{
e.printStackTrace();
} finally
{ lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Test t = new Test();
Thread thread = new Thread(t);
thread.start();
Thread.sleep(2000);
lock.lock();
condition.signal(); lock.unlock();
}
}
上述例子很簡單,讓一個線程await住,讓主線程去喚醒它。condition.await()/signal只能在得到鎖以後使用。
1.3.Semaphore
對於鎖來說,它是互斥的排他的。意思就是,只要我獲得了鎖,沒人能再獲得了。
而對於Semaphore來說,它允許多個線程同時進入臨界區。可以認為它是一個共享鎖,但是共享的額度是有限制的,額度用完了,其他沒有拿到額度的線程還是要阻塞在臨界區外。當額度為1時,就相等於lock
下面舉個例子:
package test;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class Test implements Runnable{ final Semaphore semaphore = new Semaphore(5); @Override
public void run()
{ try
{
semaphore.acquire();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getId() + ” done”);
} catch (Exception e)
{
e.printStackTrace();
}finally {
semaphore.release();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(20); final Test t = new Test(); for (int i = 0; i 20; i++)
{
executorService.submit(t);
}
}
}
有一個20個線程的線程池,每個線程都去 Semaphore的許可,Semaphore的許可只有5個,運行後可以看到,5個一批,一批一批地輸出。
當然一個線程也可以一次申請多個許可
public void acquire(int permits) throws InterruptedException
1.4 ReadWriteLock
ReadWriteLock是區分功能的鎖。讀和寫是兩種不同的功能,讀-讀不互斥,讀-寫互斥,寫-寫互斥。
這樣的設計是並發量提高了,又保證了數據安全。
使用方式:
private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock = readWriteLock.writeLock();
詳細例子可以查看 Java實現生產者消費者問題與讀者寫者問題,這裡就不展開了。
1.5 CountDownLatch
倒數計時器
一種典型的場景就是火箭發射。在火箭發射前,為了保證萬無一失,往往還要進行各項設備、儀器的檢查。 只有等所有檢查完畢後,引擎才能點火。這種場景就非常適合使用CountDownLatch。它可以使得點火線程
,等待所有檢查線程全部完工後,再執行
使用方式:
static final CountDownLatch end = new CountDownLatch(10);
end.countDown();
end.await();
示意圖:
一個簡單的例子:
package test;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test implements Runnable{ static final CountDownLatch countDownLatch = new CountDownLatch(10); static final Test t = new Test(); @Override
public void run()
{ try
{
Thread.sleep(2000);
System.out.println(“complete”);
countDownLatch.countDown();
} catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i 10; i++)
{
executorService.execute(t);
}
countDownLatch.await();
System.out.println(“end”);
executorService.shutdown();
}
}
主線程必須等待10個線程全部執行完才會輸出”end”。
1.6 CyclicBarrier
和CountDownLatch相似,也是等待某些線程都做完以後再執行。與CountDownLatch區別在於這個計數器可以反覆使用。比如,假設我們將計數器設置為10。那麼湊齊第一批1 0個線程後,計數器就會歸零,然後接着湊齊下一批10個線程
使用方式:
public CyclicBarrier(int parties, Runnable barrierAction) barrierAction就是當計數器一次計數完成後,系統會執行的動作await()
示意圖:
下面舉個例子:
package test;import java.util.concurrent.CyclicBarrier;public class Test implements Runnable{ private String soldier; private final CyclicBarrier cyclic; public Test(String soldier, CyclicBarrier cyclic)
{ this.soldier = soldier; this.cyclic = cyclic;
} @Override
public void run()
{ try
{ //等待所有士兵到齊
cyclic.await();
dowork(); //等待所有士兵完成工作
cyclic.await();
} catch (Exception e)
{ // TODO Auto-generated catch block
e.printStackTrace();
}
} private void dowork()
{ // TODO Auto-generated method stub
try
{
Thread.sleep(3000);
} catch (Exception e)
{ // TODO: handle exception
}
System.out.println(soldier + “: done”);
} public static class BarrierRun implements Runnable
{ boolean flag; int n; public BarrierRun(boolean flag, int n)
{ super(); this.flag = flag; this.n = n;
} @Override
public void run()
{ if (flag)
{
System.out.println(n + “個任務完成”);
} else
{
System.out.println(n + “個集合完成”);
flag = true;
}
}
} public static void main(String[] args)
{ final int n = 10;
Thread[] threads = new Thread[n]; boolean flag = false;
CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));
System.out.println(“集合”); for (int i = 0; i n; i++)
{
System.out.println(i + “報道”);
threads[i] = new Thread(new Test(“士兵” + i, barrier));
threads[i].start();
}
}
}
打印結果:
集合
士兵5: done士兵7: done士兵8: done士兵3: done士兵4: done士兵1: done士兵6: done士兵2: done士兵0: done士兵9: done10個任務完成
1.7 LockSupport
提供線程阻塞原語
和suspend類似
LockSupport.park();
LockSupport.unpark(t1);
與suspend相比 不容易引起線程凍結
LockSupport的思想呢,和 Semaphore有點相似,內部有一個許可,park的時候拿掉這個許可,unpark的時候申請這個許可。所以如果unpark在park之前,是不會發生線程凍結的。
下面的代碼是[高並發Java 二] 多線程基礎中suspend示例代碼,在使用suspend時會發生死鎖。
而使用 LockSupport則不會發生死鎖。
另外
park()能夠響應中斷,但不拋出異常。中斷響應的結果是,park()函數的返回,可以從Thread.interrupted()得到中斷標誌。
在JDK當中有大量地方使用到了park,當然LockSupport的實現也是使用unsafe.park()來實現的。
public static void park() { unsafe.park(false, 0L);
}
1.8 ReentrantLock 的實現
下面來介紹下ReentrantLock的實現,ReentrantLock的實現主要由3部分組成:
CAS狀態
等待隊列
park()
ReentrantLock的父類中會有一個state變量來表示同步的狀態
/**
* The synchronization state.
*/
private volatile int state;
通過CAS操作來設置state來獲取鎖,如果設置成了1,則將鎖的持有者給當前線程
final void lock() { if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread()); else
acquire(1);
}
如果拿鎖不成功,則會做一個申請
public final void acquire(int arg) { if (!tryAcquire(arg)
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先,再去申請下試試看tryAcquire,因為此時可能另一個線程已經釋放了鎖。
如果還是沒有申請到鎖,就addWaiter,意思是把自己加到等待隊列中去
其間還會有多次嘗試去申請鎖,如果還是申請不到,就會被掛起
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); return Thread.interrupted();
}
同理,如果在unlock操作中,就是釋放了鎖,然後unpark,這裡就不具體講了。
2. 並發容器及典型源碼分析
2.1 ConcurrentHashMap
我們知道HashMap不是一個線程安全的容器,最簡單的方式使HashMap變成線程安全就是使用Collections.synchronizedMap,它是對HashMap的一個包裝
public static Map m=Collections.synchronizedMap(new HashMap());
同理對於List,Set也提供了相似方法。
但是這種方式只適合於並發量比較小的情況。
我們來看下synchronizedMap的實現
它會將HashMap包裝在裡面,然後將HashMap的每個操作都加上synchronized。
由於每個方法都是獲取同一把鎖(mutex),這就意味着,put和remove等操作是互斥的,大大減少了並發量。
下面來看下ConcurrentHashMap是如何實現的
在 ConcurrentHashMap內部有一個Segment段,它將大的HashMap切分成若干個段(小的HashMap),然後讓數據在每一段上Hash,這樣多個線程在不同段上的Hash操作一定是線程安全的,所以只需要同步同一個段上的線程就可以了,這樣實現了鎖的分離,大大增加了並發量。
在使用ConcurrentHashMap.size時會比較麻煩,因為它要統計每個段的數據和,在這個時候,要把每一個段都加上鎖,然後再做數據統計。這個就是把鎖分離後的小小弊端,但是size方法應該是不會被高頻率調用的方法。
在實現上,不使用synchronized和lock.lock而是盡量使用trylock,同時在HashMap的實現上,也做了一點優化。這裡就不提了。
2.2 BlockingQueue
BlockingQueue不是一個高性能的容器。但是它是一個非常好的共享數據的容器。是典型的生產者和消費者的實現。
JAVA高並發問題,大數據,頻繁I/O操作。
建議採用緩存處理,按照你說的這種數據量,基於redis的緩存完全可以滿足,存取速度可以10W+的,另外,擬採用的hashMap 是ConcurrentHashMap還是其他,頁面展示是增量查詢還是直接所有的再查詢一次,socket數據接收你是用的netty還是mina,這都需要經過仔細的斟酌考慮設計的。有這麼大的並發的需求,完全可以考慮做分布式集群的,估計這只是領導想要的目標吧
java並發包有哪些類
1、CyclicBarrier
一個同步輔助類,允許一組線程相互等待,直到這組線程都到達某個公共屏障點。該barrier在釋放等待線程後可以重用,因此稱為循環的barrier。
來個示例:
[java] view plain copy
package test;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Recipes_CyclicBarrier {
public static CyclicBarrier barrier = new CyclicBarrier(10);
public static void main(String[] args){
ExecutorService executor = Executors.newCachedThreadPool();//FixedThreadPool(10);
for(int i=1;i=10;i++){
executor.submit(new Thread(new Runner(i+”號選手”)));
}
executor.shutdown();
}
}
class Runner implements Runnable{
private String name;
public Runner(String name){
this.name = name;
}
@Override
public void run() {
System.out.println(name + “準備好了。”);
try {
Recipes_CyclicBarrier.barrier.await(); //此處就是公共屏障點,所有線程到達之後,會釋放所有等待的線程
} catch (Exception e) {
}
System.out.println(name + “起跑!”);
}
}
2、CountDownLatch
CountDownLatch和CyclicBarrier有點類似,但是還是有些區別的。CountDownLatch也是一個同步輔助類,它允許一個或者多個線程一直等待,直到正在其他線程中執行的操作完成。它是等待正在其他線程中執行的操作,並不是線程之間相互等待。CountDownLatch初始化時需要給定一個計數值,每個線程執行完之後,必須調用countDown()方法使計數值減1,直到計數值為0,此時等待的線程才會釋放。
來個示例:
[java] view plain copy
package test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchDemo {
public static CountDownLatch countDownLatch = new CountDownLatch(10);//初始化計數值
public static void main(String[] args){
ExecutorService executor = Executors.newCachedThreadPool();//FixedThreadPool(10);
for(int i=1;i=10;i++){
executor.submit(new Thread(new Runner1(i+”號選手”)));
}
executor.shutdown();
}
}
class Runner1 implements Runnable{
private String name;
public Runner1(String name){
this.name = name;
}
@Override
public void run() {
System.out.println(name + “準備好了。”);
CountDownLatchDemo.countDownLatch.countDown(); //計數值減1
try {
CountDownLatchDemo.countDownLatch.await();
} catch (Exception e) {
}
System.out.println(name + “起跑!”);
}
}
3、CopyOnWriteArrayList CopyOnWriteArraySet
CopyOnWriteArrayList CopyOnWriteArraySet是並發容器,適合讀多寫少的場景,如網站的黑白名單設置。缺點是內存佔用大,數據一致性的問題,CopyOnWrite容器只能保證數據最終的一致性,不能保證數據實時一致性。鑒於它的這些缺點,可以使用ConcurrentHashMap容器。
實現原理:新增到容器的數據會放到一個新的容器中,然後將原容器的引用指向新容器,舊容器也會存在,因此會有兩個容器佔用內存。我們也可以用同樣的方式實現自己的CopyOnWriteMap。
4、ConcurrentHashMap
ConcurrentHashMap同樣是一個並發容器,將同步粒度最小化。
實現原理:ConcurrentHashMap默認是由16個Segment組成,每個Segment由多個Hashtable組成,數據變更需要經過兩次哈希算法,第一次哈希定位到Segment,第二次哈希定位到Segment下的Hashtable,容器只會將單個Segment鎖住,然後操作Segment下的Hashtable,多個Segment之間不受影響。如果需要擴容不是對Segment擴容而是對Segment下的Hashtable擴容。雖然經過兩次哈希算法會使效率降低,但是比鎖住整個容器效率要高得多。
5、BlockingQueue
BlockingQueue只是一個接口,它的實現類有ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、DelayQueue、LinkedBlockingDeque。
ArrayBlockingQueue:由數據支持的有界阻塞隊列。
LinkedBlockingQueue:基於鏈接節點、範圍任意的阻塞隊列。
PriorityBlockingQueue:無界阻塞隊列。
SynchronousQueue:一種阻塞隊列,其中每個插入操作必須等待另一個線程的對應移除操作。
DelayQueue:Delayed元素的一個無界阻塞隊列。
LinkedBlockingDeque:基於鏈接節點、範圍任意的雙端阻塞隊列,可以在隊列的兩端添加、移除元素。
6、Lock
Lock分為公平鎖和非公平鎖,默認是非公平鎖。實現類有ReetrantLock、ReetrantReadWriteLock,都依賴於AbstractQueuedSynchronizer抽象類。ReetrantLock將所有Lock接口的操作都委派到Sync類上,Sync有兩個子類:NonFairSync和FaiSync,通過其命名就能知道分別處理非公平鎖和公平鎖的。AbstractQueuedSynchronizer把所有請求構成一個CLH隊列,這裡是一個虛擬隊列,當有線程競爭鎖時,該線程會首先嘗試是否能獲取鎖,這種做法對於在隊列中等待的線程來說是非公平的,如果有線程正在Running,那麼通過循環的CAS操作將此線程增加到隊尾,直至添加成功。
7、Atomic包
Atomic包下的類實現了原子操作,有對基本類型如int、long、boolean實現原子操作的類:AtomicInteger、AtomicLong、AtomicBoolean,如果需要對一個對象進行原子操作,也有對對象引用進行原子操作的AtomicReference類,還有對對象數組操作的原子類:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。原子操作核心思想是CAS操作,然後調用底層操作系統指令來實現。
原創文章,作者:XFMG,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/134857.html