java核心技術(shù)總結(jié)八--多線程1、多線程程序在較低的層次上擴(kuò)展了多任務(wù)的概念: 一個程序同時執(zhí)行多個任務(wù)。通
java核心技術(shù)總結(jié)八--多線程 1、多線程程序在較低的層次上擴(kuò)展了多任務(wù)的概念: 一個程序同時執(zhí)行多個任務(wù)。通常,每一個任務(wù)稱為一個線程,它是線程控制的簡稱。可以同時運(yùn)行一個以上線程的程序稱為多線程程序。 2、每個進(jìn)程擁有自己的一整套變量,而線程則共享數(shù)據(jù)。共享變量使線程之間的通信比進(jìn)程之間的通信更有效,更容易。 3、sleep方法是Thread類的靜態(tài)方法,用于暫停當(dāng)前線程的活動,調(diào)用Thread.sleep()方法不會創(chuàng)建一個新線程。 4、并行運(yùn)行多個任務(wù)的操作: 1)、將任務(wù)代碼移到實現(xiàn)了Runnable接口的類的run方法中 2)、創(chuàng)建一個類對象 3)、由Runnable創(chuàng)建一個Thread對象 4)、啟動線程 5、interrupt 、interrupted 和isInterrunpted的區(qū)別: interrupt方法是向線程發(fā)送中斷請求,線程的中斷狀態(tài)將被設(shè)置為true; interrupted方法是一個靜態(tài)方法,它檢測當(dāng)前的線程是否被中斷,而且調(diào)用interrupted方法會清除該線程的中斷狀態(tài).它將當(dāng)前線程的中斷狀態(tài)重置為false; isInterrupted方法是一個實例方法,可以用來檢驗是否有線程被中斷,調(diào)用這個方法不會改變中斷狀態(tài) 6、線程狀態(tài): new(新生)? 用new thread()創(chuàng)建一個新線程 runnable(可運(yùn)行) 一旦調(diào)用start()方法,線程就處于runnable狀態(tài) blocked(被阻塞) waiting(等待) Timed waiting(計時等待) 當(dāng)線程處于被阻塞或者等待狀態(tài)時,它暫時不活動,它不運(yùn)行任何代碼且消耗最少的資源,直到線程調(diào)度器重新激活它。 當(dāng)一個線程試圖獲取一個內(nèi)部的對象鎖,而該鎖被其他線程持有,則該線程進(jìn)入阻塞狀態(tài); 當(dāng)線程等待另一個線程通知調(diào)度器一個條件時,它自己進(jìn)入等待狀態(tài)。 有幾個方法有一個超時參數(shù),調(diào)用它們導(dǎo)致線程進(jìn)入計時等待狀態(tài)。這一狀態(tài)將一直保持直到超時期滿或者接受到適當(dāng)?shù)耐ㄖ?。帶有超時參數(shù)的方法有:Thread.sleep,Object.wait,Thread.join,Lock.tryLock,Condition.await Terminated(被終止) 線程被終止的原因: 1)、因為run方法正常退出而自然死亡 2)、因為一個沒有捕獲的異常終止了run方法而意外死亡 7、線程屬性: 1)、線程優(yōu)先級:優(yōu)先級順序是從1到10; 2)、守護(hù)線程: 其唯一的好處是為其他線程提供服務(wù); 3)、線程組: 線程組類實現(xiàn)Thread.UncaughtExceptionHandler接口。它的uncaughtException方法做如下操作: (1)、如果該線程組有父線程,則父線程組的uncaughtException方法被調(diào)用; (2)、否則,如果Thread.getDefaultExceptionHandler方法返回一個非空的處理器,則調(diào)用該處理器; (3)、否則,如果Throwable是ThreadDeath的一個實例,什么都不做; (4)、否則,線程的名字以及Throwable的棧蹤跡被輸出到System.err上; 4)、處理未捕獲異常的處理器 8、同步 8.1、鎖對象:ReentrantLock類對象。 每一個對象有自己的ReentrantLock對象,如果兩個線程試圖訪問同一個對象,那么鎖以串行的方式提供服務(wù)。 鎖是可重入的,因為線程可以重復(fù)的獲得已經(jīng)持有的鎖。鎖保持一個持有技術(shù)來跟蹤 對lock方法的嵌套調(diào)用。線程在每一個調(diào)用lock都要調(diào)用unlock來釋放鎖。 8.2、條件對象:通常線程進(jìn)入臨界區(qū),卻發(fā)現(xiàn)在某一條件滿足之后它才能執(zhí)行,要使用一個條件對象來管理那些已經(jīng)獲得了一個鎖但是卻不能做有用工作的線程。條件對象經(jīng)常被稱為條件變量。 一個鎖對象可以有一個或多個相關(guān)的條件對象,可以用newCondition方法獲得一個條件對象。當(dāng)方法的條件不能滿足時,當(dāng)前線程被阻塞,并放棄了鎖。等待獲得鎖的線程和調(diào)用await方法的線程存在本質(zhì)上的區(qū)別。一旦一個線程調(diào)用await方法,它進(jìn)入該條件的等待集。當(dāng)鎖可用時,該線程不能馬上解除阻塞。相反,它處于阻塞狀態(tài),直到另一個線程調(diào)用同一個條件上的signalAll方法時為止。 當(dāng)一個線程調(diào)用await時,它沒有辦法重新激活自身。在對象的狀態(tài)有利于等待線程的方向改變時調(diào)用signalAll。signalAll不會立即激活一個等待線程,它僅僅解除等待線程的阻塞。 當(dāng)一個線程擁有某個條件的鎖時,它僅僅可以在該條件上調(diào)用await,signalAll 或signal方法。 9、鎖和條件的關(guān)鍵之處: 1)、鎖用來保護(hù)代碼片段,任何時刻只能有一個線程執(zhí)行被保護(hù)的代碼 2)、鎖可以管理試圖進(jìn)入被保護(hù)代碼段的線程 3)、鎖可以擁有一個或多個相關(guān)的條件對象 4)、每個條件對象管理那些已經(jīng)進(jìn)入被保護(hù)的代碼段但還不能運(yùn)行的線程。 10、synchronized關(guān)鍵字 如果一個方法用synchronized關(guān)鍵字聲明,那么對象的鎖將保護(hù)整個方法。這個鎖也被稱為內(nèi)部對象鎖,它只有一個相關(guān)條件。wait方法添加一個線程到等待集中,notify/notifyAll方法解除等待線程的阻塞狀態(tài)。 wait、notifyAll以及notify方法是Object類的final方法,它和Condition類的方法await、signalAll和signal功能類似。 內(nèi)部鎖和條件存在一些局限性: 1)、不能中斷一個正在試圖獲得鎖的線程; 2)、試圖獲得鎖時不能設(shè)定超時; 3)、每個鎖僅有單一的條件,可能是不夠的。 11、使用synchronized和 lock/condition的建議: 1、最好既不用用lock/condition也不要使用synchronized關(guān)鍵字。在許多情況下可以使用java.util.concurrent包中的一種機(jī)制,它會處理所有的加鎖。 2、如果synchronized關(guān)鍵字適合你的程序,則盡量使用它,這樣可以減少編寫的代碼數(shù)量,減少出錯的幾率。 3、如果特別需要lock/condition結(jié)構(gòu)提供的獨(dú)有特性時,才使用lock/condition. 12、同步阻塞 同步阻塞是另外一種獲得鎖的方式。 有時程序員使用一個對象的鎖來實現(xiàn)額外的原子操作,實際上稱為客戶端鎖定??蛻舳随i定是非常脆弱的,通常不推薦使用。 13、監(jiān)視器 1、監(jiān)視器具有的特性有: 監(jiān)視器是只包含私有域的類 每個監(jiān)視器類的對象有一個相關(guān)的鎖 使用該鎖對所有的方法進(jìn)行加鎖 該鎖可以有任意多個相關(guān)條件 2、java對象不同于監(jiān)視器的方面: 域不要求必須是private 方法不要求必須是synchronized 內(nèi)部鎖對客戶是可用的 14、同步格言: 如果向一個變量寫入值,而這個變量接下來可能會被另外一個線程讀取,或者,從一個變量讀取,而這個變量可能是之前被另一個線程寫入的,此時必須使用同步。 15、volatile volatile關(guān)鍵字為實例域的同步訪問提供了一種免鎖機(jī)制,如果聲明一個域為volatile,則編譯器和虛擬機(jī)就知道該域是可能被另外一個線程并發(fā)更新的。 volatile變量不能提供原子性 16、域的并發(fā)訪問是安全的條件: 域是final,并且在構(gòu)造器調(diào)用完成之后被訪問 對域的訪問由公有的鎖進(jìn)行保護(hù) 域是volatile的 17、鎖測試和超時 tryLock方法試圖申請一個鎖,在成功獲得鎖后返回true。否則返回false。tryLock允許程序打破死鎖。 18、讀寫鎖 使用讀/寫鎖的必要步驟: 構(gòu)造一個ReentrantReadWriteLock對象; 抽取讀鎖和寫鎖; 對所有的訪問者加讀鎖; 對所有的修改者加寫鎖;寫者線程必須是互斥訪問的。 19、stop和suspend方法被棄用的原因 stop方法天生就不安全,該方法會終止所有未結(jié)束的方法,包括run方法。當(dāng)線程被終止,立即釋放它鎖住的所有對象的鎖。 suspend不會破壞對象,但是很容易造成死鎖。但是如果用suspend掛起一個持有一個鎖的線程,則該鎖在恢復(fù)之前是不可用的。如果調(diào)用suspend方法的線程試圖獲得同一個鎖,則程序死鎖。被掛起的線程等著被恢復(fù),而將其掛起的線程等待獲得鎖。 20、阻塞隊列 阻塞隊列的幾個變種: LinkedBlockingQueue: 容量沒有上邊界,但是可以選擇指定最大容量。它是一個雙端的版本。 ArrayBlockingQueue:在構(gòu)造時需要指定容量,并且有一個可選的參數(shù)來指定是否需要公平性。 PriorityBlockingQueue: 它是一個帶優(yōu)先級的隊列,而不是先進(jìn)先出隊列。元素按照它們的優(yōu)先級順序被移出。該隊列的容量沒有上界。 DelayQueue 阻塞隊列的方法: add???????? 添加一個元素。?????????????? ? ? ?? 如果隊列滿,則拋出異常 element? 返回隊列的頭元素。???????????????? 如果隊列空,拋出NoSuchElementException異常 offer? ? ? ? 添加一個元素并返回true。?????? 如果隊列滿,返回false peek ? ? ? 返回隊列的頭元素。???????????????? 如果隊列空,則返回null poll? ? ? ?? 移出并返回隊列的頭元素。??????? 如果隊列空,則返回null put? ? ? ?? 添加一個元素。?????????????????????? 如果隊列滿,則阻塞 remove?? 移出并返回頭元素。??????????????? 如果隊列空,則拋出異常 take??????? 移出并返回頭元素。??????????????? 如果隊列空,則阻塞 poll和peek方法返回空來指示失敗,因此向這些隊列中插入null值是非法的。 21、線程安全的集合 java.util.concurrent包提供了映像、有序集和隊列的高效實現(xiàn): ConcurrentHashMap、ConcurrentSkipListMap、 ConcurrentLinkedQueue。這些集合使用復(fù)雜的算法,通過允許并發(fā)的訪問數(shù)據(jù)結(jié)構(gòu)不同的部分來使競爭極小化。集合返回弱一致性的迭代器。 并發(fā)的散列映像表,可高效的支持大量的讀者和一定數(shù)量的寫者。默認(rèn)情況下,假定可以有多達(dá)16個寫者線程同時執(zhí)行。 ConcurrentHashMap和ConcurrentSkipListMap類有相應(yīng)的方法用于原子性的關(guān)聯(lián)插入以及關(guān)聯(lián)刪除。putIfAbsent方法自動地添加新的關(guān)聯(lián),前提是原來沒有這一關(guān)聯(lián)。 22、寫數(shù)據(jù)的拷貝 CopyOnWriteArrayList和CopyOnWriteArraySet是線程安全的集合,其中所有的修改線程對底層數(shù)組進(jìn)行復(fù)制。 23、舊的線程安全的集合 Vector和Hashtable類提供了線程安全的動態(tài)數(shù)組和散列表的實現(xiàn)。在java1.2中,這些類被棄用了,取而代之的是ArrayList和HashMap類。這些類不是線程安全的,而集合庫中提供了不同的機(jī)制。任何集合類通過使用同步包裝器變成線程安全的。 24、執(zhí)行器 使用線程池的理由: 1、構(gòu)建一個新的線程是有一定代價的,因為涉及與操作系統(tǒng)的交互。如果程序中創(chuàng)建了大量的生命期很短的線程,應(yīng)該使用線程池。一個線程池中包含許多準(zhǔn)備運(yùn)行的空閑線程。當(dāng)Runnable對象交給線程池,就會有一個線程調(diào)用run方法。當(dāng)run方法退出時,線程不會死亡,而是在池中準(zhǔn)備為下一個請求提供服務(wù)。 2、減少并發(fā)線程的數(shù)目 25、執(zhí)行者工廠方法 方法???????????????????????????????????? ? ? ? ? 描述 newCachedThreadPool?????? 必要時創(chuàng)建新線程,空閑線程會被保留60秒 newFixedThreadPool?? 該池包含固定數(shù)量的線程,空閑線程會一直被保留 newSingleThreadExecutor? 只有一個線程的"池",該線程順序執(zhí)行每一個提交的任務(wù) newScheduledThreadPool?? 用于預(yù)定執(zhí)行而構(gòu)建的固定線程池,替代java.util.Timer newSingleThreadScheduledExecutor?? 用于預(yù)定執(zhí)行而構(gòu)建的單線程"池" 26、在使用線程池時應(yīng)該做的事情: 1)、調(diào)用Executors類中靜態(tài)的方法newCachedThreadPool或newFixedThreadPool 2)、調(diào)用submit提交Runnable或callable對象 3)、如果想要取消一個任務(wù),或如果提交Callable對象,那就要保存好返回的Future對象 4)、當(dāng)不再提交任何任務(wù)時,調(diào)用shutdown。 27、同步器 1)、fly信號量 2)、倒計時門栓 3)、障柵 4)、交換器。當(dāng)兩個線程在同一個數(shù)據(jù)緩沖區(qū)的兩個實例上工作的時候,就可以使用交換器。典型的情況是:一個線程向緩沖區(qū)填入數(shù)據(jù),另一個線程消耗這些數(shù)據(jù)。當(dāng)它們都完成以后,相互交換緩沖區(qū)。 5)、同步隊列。它是一種將生產(chǎn)者與消費(fèi)者線程配對的機(jī)制。當(dāng)一個線程調(diào)用SynchronousQueue的put方法時,它會阻塞直到另一個線程調(diào)用take方法為止。
相關(guān)新聞 |
|