【讀書筆記】TiJava——併發

函數型語言

•Erlang:大量使用併發的時候使用

協作式和搶佔式

•協作多線程、協作式系統
–每個任務自動放棄控制
–同時執行的線程數無限制
–適合處理大量的仿真元素

線程與設計

•線程使你能夠創建更加鬆耦合的設計
•線程的使用
–實現Runable
–使用Thread
•Thread.yield——不能依賴
–將cpu從一個線程讓給另一個線程

Thread的垃圾回收

•在它的任務退出run並死亡之前,垃圾回收器無法清除它

使用Executor

•可以管理Thread對象
•使用多線程的優選方法
•首選CachedThreadPool
–Executors.newCachedThreadPool()
•在線程內部,通過Thread.currentThread獲得當前驅動任務線程的對象

SingleThreadExecutor

•如果向SingleThreadExecutor提交多個任務,這些任務會排隊,每個任務按順序執行

使用Callable從任務中返回值

•泛型
•必須使用ExecutorService.submit執行任務
•submit方法返回參數化的Future對象

休眠

•Old-style:
–Thread.sleep(100)
•Java5/6-style
–TimeUnit.MILLISECONDS.sleep(100);
•sleep會拋出InterruptedException,必須在run中捕獲並處理異常,不能跨線程傳播回main,因爲在main中不能捕獲由線程拋出的異常

優先級

•唯一可移植的優先級:
–Thread.MAX_PRIORITY
–Thread.MIN_PRIORITY
–Thread.NORM_PRIORITY
•線程初始化優先級
–Thread.NORM_PRIORITY
•優先級修改
–在run裏面修改,Thread.currentThread().setPriority(priority);
後臺線程daemon——不推薦

•Thread.setDaemon(true)
•當非後臺線程結束後,後臺線程自動結束,且不會執行finally子句,即,程序不能正常關閉。因此不推薦使用!
•使用ThreadFactory爲ExecutorService創建後臺線程
•後臺線程創建的線程自動都是後臺線程

join

•等待某線程結束後再繼續執行
–A任務裏調用B.join,說明A要等待B結束
•替代者:CyclicBarrier

異常捕獲

•在run中的catch中捕獲中斷異常時,中斷標誌總是false的。因爲已經被清除了。。。
•因爲在main中不能捕獲由線程拋出的異常,可以使用Thread.setDefaultUncaughtExceptionHandler設置默認的未捕獲異常處理器

同步—synchronized

•對象自動含有鎖
•當對象上任一synchronized方法被調用時,對象即被鎖,其他線程只能等待鎖被釋放,才能繼續調用對象上synchronized方法
•即一個對象的所有synchronized共享一個鎖
•類也有鎖, synchronizedstatic可以鎖靜態數據

何時同步

•每個訪問臨界共享資源的方法都必須被同步
•Biran同步規則
–如果你正在寫一個變量,它可能被另一個線程讀取,或正在讀一個被另一個線程寫過的變量,那必須使用同步,並且,讀寫線程必須用相同的監視器鎖同步。

顯示鎖Lock

•Lock.lock/Lock.unlock
•在try中return,在finally中unlock
•Lock.tryLock嘗試獲取鎖,成功就鎖,失敗就繼續執行
•通常只在解決特殊問題時使用

原子性

•不正確的認識:
–原子操作不需要同步控制!
•除long和double之外的所有基本類型的簡單操作(讀/寫)是原子操作
•當定義long或double變量時使用volatile,也可以獲得原子性

可視性

•一個任務作出的修改,對另一個任務不一定是可視的,特別是做多處理器系統中,例如修改只暫存在本處理器的緩存中
•使用volatile,使修改對多cpu可見

volatilesynchronized

•只要對域做了寫操作,就會被寫入主存,其他所有任務的讀操作就能讀到這個修改。
•同步synchronized也會向主存中刷新,因此如果用了synchronized,就不必使用volatile
•首選synchronized,是最安全的
•在任何時候,如果有多個線程讀寫某個對象,就使用synchronized方法

原子類

•AtomicInteger/Long等類,可以代替鎖
•對常規變成很少用,在性能調優時使用
•只在特殊情況下使用Atomic類,使用鎖(synchronized 和Lock)更安全

使用同步控制塊

•不會防止訪問某整個方法,可顯著提高性能
•synchronized(object){…..}得到的是object的鎖
•一般使用synchronized(this){…..}

ThreadLocal

•根除對變量的共享
•爲使用相同變量的每個線程創建不同的副本
•ThreadLocal通常是靜態存儲域

中斷被阻塞的線程

•Thread.interrupt
•在ExecutorService上調用shutdownNow,會發送interrupt給它的所有線程
•使用ES的submit而不是executor來啓動任務,通過返回的Future.cancle(true),可以發送interrupt給Future代表的線程中斷線程。
•當任務試圖執行已經被中斷的阻塞時,將拋出InterruptedException

阻塞都能被中斷嗎?

•sleep能被中斷
•IO阻塞不能被中斷
–解決辦法:關閉底層資源
–更人性化的方案:使用nio
•synchronized阻塞不能被中斷
–使用顯示Lock,在Lock上的阻塞可以被中斷。Lock.lockInterruptibly方法
•任何不可中斷的阻塞,都可能引起死鎖

Run函數慣用法

public void run() {
int id=0;
try {
while (!Thread.interrupted()) {
// point1
Resource n1 = new Resource(++id);
// Start try-finally immediately after definition
// of n1, to guarantee proper cleanup of n1:
try {
print("Sleeping");
TimeUnit.SECONDS.sleep(1);
// point2
Resource n2 = new Resource(++id);
// Guarantee proper cleanup of n2:
try {
print("Calculating");
// A time-consuming, non-blocking operation:
for (int i = 1; i < 2500000; i++)
d = d + (Math.PI + Math.E) / d;
print("Finished time-consuming operation");
} finally {
 	n2.cleanup();
}
} finally {
	n1.cleanup();
}
}
print("Exiting via while() test");
} catch (InterruptedException e) {
	print("Exiting via InterruptedException");
}
}

waitsleepyield

•都會掛起線程
•wait會釋放鎖,表示某事已經做完,但sleep和yield不會
•wait、notify必須在synchronized方法/塊中被調用(否則會拋出異常),sleep可以在非synchronized方法/塊中被調用

汽車塗蠟-拋光

•塗蠟-WaxOn
–第一步:塗蠟
–進入下一場塗蠟之前,先等待拋光完成
•拋光-WaxOff
–進行拋光之前,必須先等待塗蠟完成
–第二步:拋光

等待慣用法:while

•使用while檢查感興趣的條件標誌,當被喚醒時若發現條件不滿足時,繼續等待,直到條件滿足
while(condition){
wait();
}

notifynotifyAll

•一般使用notifyAll,防止漏通知
•notifyAll只通知等待同一個對象鎖的任務,而不會通知到等待其他對象鎖定任務

顯式的LockCondition對象

•Lock.newCondition
•Condition.await/signal/signalAll
•在調用condition的方法時,必須先獲得lock

阻塞隊列

•阻塞隊列可以解決大量的問題,且與wait和notify方式比起來簡單可靠得多…..
•Java.util.concurrent.BlockingQueue
•阻塞隊列本身具有同步控制功能
•消除類之間的耦合

死鎖

•哲學家就餐
•四個條件同時滿足
–存在互斥資源
–持有且等待
–資源不能搶佔
–循環等待(以某種特定的順序獲取資源)
•防止死鎖
–破壞某個條件(循環等待最容易破壞掉)

CountDownLatch

•用於同步一組任務等待另一組任務完成
•方法
–await,調用該方法的線程將阻塞,直到latch的值減到0
–countDown,每調一次,latch減1

CyclicBarrier

•一組任務並行執行,但在進行下一步之前,必須等待其他任務把之前的工作都做完後,再同時進行下一步工作
•具有柵欄動作,當減到0時觸發,在new的時候指定參數Runnable
•自動進行下一輪,可多次自動重用
•CountDownLatch只能用一次

DelayQueue

•是一個無界的阻塞隊列,用於放置實現Delayed接口的元素
•通過時間到期喚醒任務
•隊列有序,排序通過實現Comparable接口實現。
•只有到期的元素才能被取出,即getDelay返回小於等於0。若無到期元素,將阻塞,直到有到期元素

PriorityBlockingQueue

•按優先級排序的阻塞隊列
•任務通過實現Comparable實現排序
•在隊列爲空時,阻塞隊列的消費/讀取任務任務

SynchronousQueue

•沒有內部容量的阻塞隊列
•每個put都必須等待一個take

ScheduledExecutor

•schedule
–運行一次
•scheduleAtFixedRate
–按頻率重複運行

 Semaphore

•計數信號量
•應用於對象池,池中對象創建代價比較高
•請求資源:acquire
•釋放資源:release

Exchanger

•兩個任務之間交換對象的柵欄
•用於生產者和消費者之間消費大對象的列表,可一步消費整個隊列
•exchange(…)

銀行出納員仿真

•場景
–對象隨機出現,並且要求由數量有限的服務器提供隨機數量的服務時間


飯店仿真



汽車組裝機器人



比較各類互斥技術

•以synchronized入手,只有在性能調優時才替換爲Lock,只有在性能方面有明確的指標時,才替換爲Atomic
•Atomic>Lock>synchronize

免鎖容器

•CopyOnWriteArrayList
–寫入時,創建新副本,原數據保持不變,仍可安全讀取,當修改完成時,一個原子操作把新數據換入,使得新的讀取操作可以看到這個修改。
•類似:
–CopyOnWriteArraySet
–ConcurrentHashMap、ConcurrentLinkedQueue
•如主要從免鎖容器讀數據,而少寫,則比synchronized對應物快,因爲加解鎖的開銷被省掉了
•SynchronizedList無論讀取者和寫入者的數量多少,都具有大致相同的性能
•CopyOnWriteArrayList在沒有寫入者時,速度快許多,在有5個寫入者時,速度依然明顯快。
•推薦使用CopyOnWrite和Concurrent….系列

樂觀鎖

•Atomic.compareAndSet
•決定在Atomic.compareAndSet失敗,必須決定做什麼事情?是很重要的問題

ReadWriteLock

•允許多個任務同時讀
•只允許一個任務寫,且寫時不允許任何讀
•一個相當複雜的工具,只有在需要提高性能時才需要用到

活動對象

•每個活動對象持有一個任務隊列,任何時刻,只有一個任務在運行

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章