第一章 走入並行世界
關於並行的兩個定律: Amdahl定律 和Gustafson定律 考慮方向不同
Java內存模型 JMM 原子性 可見性 有序性
第二章 Java並行程序基礎
線程創建: new Thread(Runable).start()
線程終止: stop 方法 會立即釋放鎖,導致數據不一致問題,已經廢棄
線程中斷: interrupt() 設置中斷標識,run方法中可以依靠這個做線程退出的邏輯處理Thread.interrupted()用來判斷
等待和通知:wait()和notify()必須在synchronized中,涉及到鎖的操作
掛起和繼續執行:suspend 和 resume 已經廢棄
等待線程結束和謙讓:
如果一個線程A依賴另外一個線程B,那就在A中執行B.join() 即 A要等待線程B結束
yield()是讓出CPU資源,但是還是會進行資源競爭.
volatile關鍵字:保證線程間的可見性和禁止執行重排(有序性)但是不能保證原子性 JMM
守護線程: daemon 後臺線程,當用戶線程都結束時,守護線程也會結束,如垃圾回收線程
第三章 JDK併發包
1.同步控制
1.1可重入鎖 ReentrantLock
ReentrantLock lock = new ReentrantLock()
lock.lock() 加鎖
lock.unlock解鎖
對同一個線程而言,可以重複的加鎖,也要解相同次數的鎖才行
中斷響應: lock.lockInterruptibly() 會對thread.interrupt()拋出異常,對其進行處理即可
限時申請鎖: lock.tryLock(time,timeUnit.xxx) 在限定時間內申請獲取鎖,成功返回true 失敗返回false
公平鎖: ReentrantLock lock = new ReentrantLock(true) 默認非公平,公平鎖需要維護一個線程申請鎖的隊列
1.2 重入鎖好搭檔: Conditon 條件
Condition condition = lock.newCondition()
condition的用法類似於object.wait() 和 object.notify()
await()方法會使當前線程等待,同時釋放當前鎖,當其他線程中使用signal()或者signalAll()方法是,線程活重新獲取鎖,繼續執行.
當前線程被中斷時也能跳出等待.
awaitUninterruptibly() 和 await()類似,但是不會中斷相應
singal()喚醒一個線程,singalAll()喚醒所有線程.
併發容器中使用兩個condition實現隊列的take和put的阻塞.見ArrayBlockingQueue源碼
1.3 允許多線程同時訪問: 信號量 Semaphore
Semaphore (int permits) 或者 Semaphore (int permits,boolean fair) 數量 和 公平
大家看方法就能猜如來如何使用了
acquire() 獲取一個憑證
acquireUninterruptibly() 獲取憑證對中斷響應
tryAcquire() 不等待獲取憑證 類似 tryLock()
tryAcquire(timeout,unit) 指定時間內獲取憑證
release() 釋放憑證
1.4 ReadWriteLock 讀寫鎖
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock ();
Lock readLock = readWriteLock.readLock(); 讀鎖
Lock writeLock = readWriteLock.writeLock(); 寫鎖
讀讀不互斥,寫寫互斥,讀寫互斥.
1.5 CountDownLatch 倒計時器
這個很常見不就寫了
1.6 循環柵欄: CyclicBarrier
和CountDownLatch類似但比其強大.
CyclicBarrier(int parties,Runnable barrieraction)
循環柵欄會阻塞 xxx.await()直到滿足數目才讓線程一起向下執行,且在此之前會執行一次 barrieraction中run方法
1.7 線程阻塞工具類: LockSupport
它可以在線程內任意位置讓線程阻塞.
LockSupport.park() 可以阻塞當前線程,類似的還有parkNanos(),parkUntil()等實現一個閒時的阻塞
LockSupprot.unpark(Thread) 釋放指定線程的阻塞
2. 線程複用:線程池
這個就不浪費筆墨了,依託底層 ThreadPoolExecutor實現了幾種常用的線程池工具,大家應該都明白的.
線程池可以根據自己的需求進行相應的自定義處理:
ThreadPoolExecutor提供了beforeExecute() ,afterExecute(),terminated() 方法對其進行控制.
線程數量選取: Nthreads = Nepu * Ucpu * ( 1 + W/C) 即 cpu數量 * 目標Cpu使用率 *(1 + 等待時間與計算時間比)
分而治之:Fork/Join框架
ForkJoinPool.submit(ForkJoinTask) 提交任務 task.fork() 執行結束後 使用 task.join收集結果
3. JDK併發容器
1. 併發集合
ConcurrenHashMap
CopyOnWriteArrayList
ConcurrentLinkedQueue
BlockingQueue
ConcurrentSkipListMap
2. 高效讀寫隊列:深度剖析 ConcurentLinkedQueue
主要通過CAS(比較交換)操作和對head及tail的算法實現高效的讀寫
3. 高效讀取: 不變模式下的CopyOnWriteArrayList
任何讀取操作沒有鎖相關操作,但是寫入時候.會加鎖,複製原來的集合寫一個新集合後替換老集合.
4. 數據共享通道: BlockingQueue
前面說過其take和put的通過兩個condition裏相互通知實現阻塞.
5.隨機數據結構:跳錶(SkipList)
維護多層鏈表,鏈表分層,通過空間換時間的方法來實現高速的查找.