java并发编程实战阅读总结(b)
5、Executor框架
Executor框架是并發集合java.util.concurrent中的一個成員。
Executor為靈活且強大的異步任務執行框架提供了基礎,還提供了對生命周期的支持,以及統計信息、應用管理機制和性能監視等機制。Executor 最早是為了解決生產者-消費者模式而引入的。提交任務相當是生產者,執行任務相當是消費者。
線程池:(翻譯的文檔):
線程池和工作者隊列密切相關,工作者線程的任務:從工作隊列中獲取一個任務,執行任務,然后返回線程池并等待下一個任務。
Executors類里面提供了一些靜態工廠,生成一些常用的線程池。
newSingleThreadExecutor:創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當于單線程串行執行所有任務。如果這個唯一的線程因為異常結束,那么會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。
newFixedThreadPool:創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那么線程池會補充一個新線程。
newCachedThreadPool:創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那么就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(或者說JVM)能夠創建的最大線程大小。
newScheduledThreadPool:創建一個大小無限的線程池。此線程池支持定時以及周期性執行任務的需求。
newSingleThreadScheduledExecutor:創建一個單線程的線程池。此線程池支持定時以及周期性執行任務的需求。
線程池Executor任務拒絕策略(翻譯的文檔):
java.util.concurrent.RejectedExecutionHandler描述的任務操作。
第一種方式直接丟棄(DiscardPolicy)
第二種丟棄最舊任務(DiscardOldestPolicy)
第三種直接拋出異常(AbortPolicy)
第四種任務將有調用者線程去執行(CallerRunsPolicy)
生命周期(翻譯的文檔):
java.util.concurrent.ExecutorService 接口對象來執行任務,該接口對象通過工具類java.util.concurrent.Executors的靜態方法來創建。 Executors此包中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。
ExecutorService擴展了Executor并添加了一些生命周期管理的方法。一個Executor的生命周期有三種狀態,運行 ,關閉 ,終止。
Executor創建時處于運行狀態。當調用ExecutorService.shutdown()后,處于關閉狀態,isShutdown()方法返回true。這時,
不應該再想Executor中添加任務,所有已添加的任務執行完畢后,Executor處于終止狀態,isTerminated()返回true。
shutdown():執行平緩的關閉過程,不再接受新的任務,同時等待已經提交的任務執行完成。
shutdownNow();執行粗暴的關閉過程,嘗試取消所有運行中的任務,并且不再啟動隊列中尚未開始啟動的任務。
awaitTermination: 這個方法有兩個參數,一個是timeout即超時時間,另一個是unit即時間單位。
這個方法會使線程等待timeout時長,當超過timeout時間后,會監測ExecutorService是否已經關閉,若關閉則返回true,否則返回false。一般情況下會和shutdown方法組合使用。
ExecutorService service = Executors. newFixedThreadPool(3); ?
? ? ? ? ?for ( int i = 0; i < 10; i++) { ?
? ? ? ? ? ? ?System. out.println( "創建線程" + i); ?
? ? ? ? ? ? ?Runnable run = new Runnable() { ?
? ? ? ? ? ? ? ? ?@Override ?
? ? ? ? ? ? ? ? ?public void run() { ?
? ? ? ? ? ? ? ? ? ? ?System. out.println( "啟動線程"); ?
? ? ? ? ? ? ? ? ?} ?
? ? ? ? ? ? ?}; ?
? ? ? ? ? ? ?// 在未來某個時間執行給定的命令 ?
? ? ? ? ? ? ?service.execute(run); ?
? ? ? ? ?} ?
? ? ? ? ?// 關閉啟動線程 ?
? ? ? ? ?service.shutdown(); ?
? ? ? ? ?// 每隔1秒監測一次ExecutorService的關閉情況. ?
? ? ? ? ?service.awaitTermination(1, TimeUnit. SECONDS); ?
? ? ? ? ?System. out.println( "all thread complete"); ?
? ? ? ? ?System. out.println(service.isTerminated()); ?
6、鎖機制引入的死鎖、活鎖和饑餓
加鎖機制的目的是保證線程安全,但如果過度使用鎖,會導致死鎖等。我們使用線程池和信號量來限制對資源的限制。但這些被限制的行為可能導致死鎖。
死鎖:進程之間互相爭奪資源導致系統無法向前運行的一種僵死狀態,如果沒有外力作用下系統很難繼續向前運行。請參考我的博客多線程。
饑餓:當線程無法訪問到它所需要的資源而不能繼續執行時,就會發生饑餓。引發饑餓的最常見資源就是CUP的時鐘周期。
活鎖:liveLock是進程的狀態還在發生變化但是不再繼續向前運行的狀態。
7、線程的開銷:
(1)、上下文切換。cpu在做線程切換的時候,需要保存當前線程執行的上下文,并且新調度進來的線程執行上下文設置為當前上下文。
發生越多的上下文切換,增加了調度開銷,并因此降低吞吐量。
(2)、內存數據的同步。synchronized發生隱式鎖競爭的地方帶來的開銷會影響其它線程的性能。
(3)、阻塞。當在鎖上發生競爭時,競爭失敗的線程會阻塞,即所謂的競態條件。JVM通過循環不斷的嘗試獲取鎖,直到成功。或者通過操作系統掛起阻塞的線程。
如果時間短,采用等待方式,如果時間長才適合采用線程掛起的方式。串行操作降低可伸縮性,并行切換上下文也會降低性能。
在鎖發生競爭時,會同時導致上面兩種問題,因此,減少鎖的競爭能夠提高性能和收縮性。在并發程序中,
對可伸縮性最主要的威脅就是獨占方式的資源鎖。兩個因素將影響鎖上面發生競爭的可能性:鎖的請求頻率以及每次持有該鎖的時間。
如果兩者的乘積很小,那么大多數獲取鎖操作都不會發生競爭。
三種方式可以降低鎖的競爭程度:
(1)、降低鎖的請求頻率。
降低線程請求鎖的頻率,可以通過鎖分解和鎖分段等技術來實現。即減小鎖的粒度。如果一個鎖同時需要保護好幾個狀態變量,那么可以把這個鎖分解成多個鎖,并且每個鎖只保護一個狀態變量,從而提高可伸縮性,并最終降低每個鎖的請求頻率。但是使用的鎖越多,發生死鎖的風險也會越高。
(2)、減少鎖的持有時間。
減少被鎖部分的加鎖時間。縮小鎖的范圍(快進快出),可以將一些與鎖無關的代碼移出同步代碼塊,尤其是開銷較大的操作,以及可能被阻塞的操作,比如I/O 操作。
(3)、減少使用獨占鎖,并發容器,讀-寫鎖,不可變對象以及原子變量。
獨占鎖是一種悲觀的技術。它假設最壞的情況發生(如果不加鎖,其它線程會破壞對象狀態),即使沒有發生最壞的情況,仍然用鎖保護對象狀態
原子性
采用鎖技術會導致對鎖的競爭導致系統性能降低。 當一個線程正在等待鎖時,它不能做任何其他事情。如果一個線程在持有鎖的情況下被延遲執行,那么所有需要這個鎖的線程都無法執行下去。
原子性是指cpu在執行每一段代碼時是不能被中斷的。對除了long和double的基本類型的數據的簡單操作都是原子的。例如a = 1; return a;
原子變量支持不用鎖保護就能原子性更新操作,其底層用CAS實現。
一共有12個原子變量,可分為4組:標量類、更新器類、數組類以及復合變量類。
最常用的原子變量就是標量類:AtomicInteger、AtomicLong、AtomicBoolean以及AtomicReference。所有類型都支持CAS。
JVM對CAS的支持
CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。
CAS典型使用模式是:首先從V中讀取A,并根據A計算新值B,然后再通過CAS以原子方式將V中的值由A變成B(只要在這期間沒有任何線程將V的值修改為其他值)。
轉載的博文:http://blog.csdn.net/csujiangyu/article/details/44002463
說明比較并交換的行為(而不是性能)的代碼:
public class SimulatedCAS {
? ? ?private int value;
? ? ?public synchronized int getValue() { return value; }
? ? public synchronized int compareAndSwap(int expectedValue, int newValue) {
? ? ? ? ?int oldValue = value;
? ? ? ? ?if (value == expectedValue)
? ? ? ? ? ? ?value = newValue;
? ? ? ? ?return oldValue;
? ? ?}
}
使用比較并交換實現計數器:
public class CasCounter {
? ? private SimulatedCAS value;
? ? public int getValue() {
? ? ? ? return value.getValue();
? ? }
? ? public int increment() {
? ? ? ? int oldValue = value.getValue();
? ? ? ? while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)
? ? ? ? ? ? oldValue = value.getValue();
? ? ? ? return oldValue + 1;
? ? }
}
非阻塞同步機制
非阻塞算法提供比synchronized機制更高的性能和可收縮性。可以使多個線程在競爭相同的數據時候不會發生阻塞。
基于鎖的算法中可能會出現各種活躍性的障礙,比如I/O 阻塞,導致其它線程都無法進行下去,導致性能下降。
總結
以上是生活随笔為你收集整理的java并发编程实战阅读总结(b)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: unity如何实现图片透视_unity
- 下一篇: 如何利用openssl来计算md4, m