国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

java多線程知識點

2019-11-15 00:14:40
字體:
來源:轉載
供稿:網友
java多線程知識點

下面是我學習多線程記錄的知識點,并沒詳細講解每個知識點,只是將重要的知識點記錄下來,有時間可以看看,如果有不對的地方,歡迎大家指出,謝謝!

1、多線程的狀態和創建方式: 線程的狀態: 1、新狀態:線程對象已經創建,還沒有在其上調用start()方法。 2、可運行狀態:當線程有資格運行,但調度程序還沒有把它選定為運行線程時線程所處的狀態。當start()方法調用時,線程首先進入可運行狀態。在線程運行之后或者從 阻塞、等待或睡眠狀態回來后,也返回到可運行狀態。 3、運行狀態:線程調度程序從可運行池中選擇一個線程作為當前線程時線程所處的狀態。這也是線程進入運行狀態的唯一一種方式。 4、等待/阻塞/睡眠狀態:這是線程有資格運行時它所處的狀態。實際上這個三狀態組合為一種,其共同點是:線程仍舊是活的,但是當前沒有條件運行。換句話說,它是可 運行的,但是如果某件事件出現,他可能返回到可運行狀態。 5、死亡態:當線程的run()方法完成時就認為它死去。這個線程對象也許是活的,但是,它已經不是一個單獨執行的線程。線程一旦死亡,就不能復生。如果在一個死去的 線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。 線程的2種創建方式:

繼承Thread類和實現Runnable接口類,使用實現接口的方式時,創建線程實例需要將接口實現類的實例傳給Thread構造函數。因為java不支持多繼承,所以建議采用實現 runnable接口的方式創建線程。


2、線程同步,關鍵字:synchronized和volatile,及其區別。

volatile知識點:

  • 根據jvm運行時刻內存的分配。其中有一個內存區域是jvm虛擬機棧,每一個線程運行時都有一個線程棧,線程棧保存了線程運行時候變量值信息。當線程訪問某一個對象 時候值的時候,首先通過對象的引用找到對應在堆內存的變量的值,然后把堆內存變量的具體值load到線程本地內存中,建立一個變量副本,之后線程就不再和對象在堆內存變量值有任何關系,而是直接修改副本變量的值,在修改完之后的某一個時刻(線程退出之前),自動把線程變量副本的值回寫到對象在堆中變量。這樣在堆中的對象的值就產生變化了。所以在多個線程共享的變量就有可能出問題。
  • 當一個共享變量被volatile修飾時,它會保證修改的值會立即被更新到主存,當有其他線程需要讀取時,它會去內存中讀取新值。所以有時候可以防止線程讀取共享變量 時,值是不正確的情況出現,但是volatile不能保證變量的原子性,如果共享變量是簡單數據類型,變量的修改不基于其自身的數值時(如x=10可以,x++不行),可以保證其原子性.因為volatile并不能保證原子性,所以慎用volatile。這里只是做個簡單的筆記,關于bolatitle更詳細的介紹可以看看下面的文章:

http://m.survivalescaperooms.com/dolphin0520/p/3920373.html#commentform http://www.infoq.com/cn/articles/java-memory-model-4/ http://blog.csdn.net/hupitao/article/details/45227891 http://m.survivalescaperooms.com/aigongsi/archive/2012/04/01/2429166.html synchronized知識點:

  • 當synchronized修改某個方法,則該方法叫同步方法,如果synchronized作用于代碼塊,則該代碼庫叫同步代碼塊。可以說同步代碼塊控制更精細。
  • java的對象鎖和類鎖:java的對象鎖和類鎖在鎖的概念上基本上和內置鎖是一致的,但是,兩個鎖實際是有很大的區別的,對象鎖是用于對象實例方法,或者一個對象實例上的,類鎖是用于類的靜態方法或者一個類的class對象上的。我們知道,類的對象實例可以有很多個,但是每個類只有一個class對象,所以不同對象實例的對象鎖是互不干擾的,但是每個類只有一個類鎖。但是有一點必須注意的是,其實類鎖只是一個概念上的東西,并不是真實存在的,它只是用來幫助我們理解鎖定實例方法和靜態方法的區別的。
  • synchronized塊寫法:synchronized(object){...}表示線程在執行的時候會將object對象上鎖(注意這個對象可以是任意類的對象,也可以使用this關鍵字)。這樣就可以自行規定上鎖對象。
  • 如果同步代碼塊中synchronized的括號內是類對象(如Object.class),則表示給類的這個方法上鎖
  • 非靜態的同步方法會將對象上鎖,但是靜態方法的鎖不屬于對象,當一個synchronized關鍵字修飾的方法同時又被static修飾時,它的鎖屬于類,它會將這個方法所在的類的Class對象上鎖。
  • 類鎖和對象鎖是兩個不一樣的鎖,控制著不同的區域,它們是互不干擾的。同樣,線程獲得對象鎖的同時,也可以獲得該類鎖,即同時獲得兩個鎖,這是允許的。
  • 如果一個對象有多個synchronized方法(包括同步代碼塊),某一時刻某個線程已經進入到了某個synchronized方法(包括同步代碼塊),那么在該方法沒有執行完畢前,其 他線程是無法訪問該對象的任何synchronized方法的(包括同步代碼塊)。但是可以訪問非同步方法和非同步代碼塊。
  • 關于sychronized的用法可以看看下面的文章:

http://zhh9106.VEvb.com/blog/2151791 http://m.survivalescaperooms.com/GnagWang/archive/2011/02/27/1966606.html http://m.survivalescaperooms.com/mengdd/archive/2013/02/16/2913806.html

  • synchronized關鍵字是防止多個線程同時執行一段代碼,那么就會很影響程序執行效率,而volatile關鍵字在某些情況下性能要優于synchronized,但是要注意volatile關鍵字是無法替代synchronized關鍵字的,因為volatile關鍵字無法保證操作的原子性。
  • 順便提下java.util.concurrent.locks包中有關于lock的用法,lock與synchronized的區別主要有:

1.synchronized隱式的加鎖和解鎖,lock顯示的加鎖和解鎖

2.使用lock時,同步代碼塊內出現異常,需要處理一些清理工作

3.lock更靈活,是更細的粒度控制

可以看看下面文章:

http://m.survivalescaperooms.com/dolphin0520/p/3923167.html


3、線程間通信,常用方法:wait、notify、notifyall、join、yeild

  • wait()、notify()和notifyAll()是Object類中的方法,并且不能被重寫,可以看jdk源碼
  • wait和notify、notifyAll方法必須與synchronized一起使用;
  • wait會釋放鎖,注意如果在同步方法或代碼塊中使用sleep,雖然sleep也是讓線程休眠,但是sleep不會釋放鎖;yield也不會釋放鎖
  • 如果只有一個線程在等待,則使用notify更好,因為它比notify效率更高。如果不能確定是用notify還是notifyall方法,則選擇使用notifyall,這可能有些浪費資源,但是更安全.notify方法只會喚醒等待中的一個線程(這個線程由jvm決定,是無法具體確定的,是隨機的,與線程優先級無關)。
  • 注意在調用notify和nofityAll的時候,并不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按線程的優先級。
  • 注意notify的遺漏通知和早期通知導致的問題,典型的同步代碼中出現如下代碼:if(condition){wait()..},這里容易出現遺漏通知的情況,此時可以將if改成while來避免此類問題。
  • 線程可以使用類ThreadLocal和InheritableThreadLocal讓特定線程變量在不同的線程中為不同的值
  • join() 方法主要是讓調用該方法的thread完成run方法里面的東西后, 在執行join()方法后面的代碼,舉個例子:你和朋友去吃飯,中途你有事,你朋友哪怕吃飽了,也會在那里等你,因為他沒帶錢等你回來結賬后才能一起走。
  • join方法只有在繼承了Thread類的線程中才能調用
  • 線程必須要start() 后再join才能起作用
  • Thread.yield()方法作用是:暫停當前正在執行的線程對象,并執行其他線程。
  • yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調度程序再次選中。
  • 結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。

可以看看以下文章: http://m.survivalescaperooms.com/riskyer/p/3263032.html


4、守護線程及線程優先級

  • 什么是守護線程(Daemon線程)?

守護線程是一個后臺運行的線程,與之對比的是用戶線程(User線程)。當正在運行的線程都是守護線程時,Java 虛擬機退出。不管守護線程執行是否完成,都直接退 出,這是特別需要注意的地方。當用戶線程存在用戶線程一直在運行,則守護線程也會一直運行。

  • 如何創建守護線程?

創建一個新線程,用setDaemon(boolean on)方法可以方便的設置線程的Daemon模式,true為Daemon模式,false為User模式。setDaemon(boolean on)方法必須 在線程啟動之前調用,當線程正在運行時調用會產生異常。isDaemon方法將測試該線程是否為守護線程。值得一提的是,當你在一個守護線程中產生了其他線程,那么這些 新產生的線程不用設置Daemon屬性,都將是守護線程,用戶線程同樣。

  • 守護線程的用處?

守護線程使用的情況較少,但并非無用,舉例來說,JVM的垃圾回收、內存管理等線程都是守護線程。還有就是在做數據庫應用時候,使用的數據庫連接池,連接池本身 也包含著很多后臺線程,監控連接個數、超時時間、狀態等等。守護線程一般是為用戶線程服務的。 可以看看以下文章: http://blog.csdn.net/ljyy2006/article/details/1901109 http://lavasoft.blog.51cto.com/62575/221845/ http://m.survivalescaperooms.com/super-d2/p/3348183.html 線程優先級:

  • 線程的優先級仍然無法保障線程的執行次序。只不過,優先級高的線程獲取CPU資源的概率較大,優先級低的并非沒機會執行。
  • 當設計多線程應用程序的時候,一定不要依賴于線程的優先級。因為線程調度優先級操作是沒有保障的,只能把線程優先級作用作為一種提高程序效率的方法,但是要保證程序不依賴這種操作。
  • 線程的優先級用1-10之間的整數表示,數值越大優先級越高,默認的優先級為5。
  • 在一個線程中開啟另外一個新線程,則新開線程稱為該線程的子線程,子線程初始優先級與父線程相同。
  • 如果該線程已經屬于一個線程組(ThreadGroup),該線程的優先級不能超過該線程組的優先級

5、線程組、線程池

  • Java提供了ThreadGroup類來控制一個線程組,一個線程組可以通過線程對象來創建,也可以由其他線程組來創建,生成一個樹形結構的線程,對線程分組是Java并發API提供的一個有趣功能。我們可以將一組線程看成一個獨立單元,并且可以隨意操縱線程組中的線程對象。比如,可以控制一組線程來運行同樣的任務,無需關心有多少線程還在運行,還可以使用一次中斷調用中斷所有線程的執行。
  • Java提供了ThreadGroup類來控制一個線程組。一個線程組可以通過線程對象來創建,也可以由其他線程組來創建,生成一個樹形結構的線程。根據《Effective Java》的說明,不再建議使用ThreadGroup。建議使用Executor。
  • java默認創建的線程都是屬于系統線程組,而同一個線程組的線程是可以相互修改對方的數據的。但如果在不同的線程組中,那么就不能“跨線程組”修改數據,可以從一定程度上保證數據安全。
  • 為什么需要線程池

網絡請求通常有兩種形式:第一種,請求不是很頻繁,而且每次連接后會保持相當一段時間來讀數據或者寫數據,最后斷開,如文件下載,網絡流媒體等。另一種形式是 請求頻繁,但是連接上以后讀/寫很少量的數 據就斷開連接。考慮到服務的并發問題,如果每個請求來到以后服務都為它啟動一個線程,那么這對服務的資源可能會造成很大的 浪費,特別是第二種情況。因為通常情況下,創建線程是需要一定的耗時的,設這個時間為T1,而連接后讀/寫服務的時間為T2,當T1>>T2時,我們就應當考慮一種策略或者 機制來控制,使得服務對于第二種請求方式也能在較低的功耗下完成。 通常,我們可以用線程池來解決這個問題,首先,在服務啟動的時候,我們可以啟動好幾個線程,并用一個容器(如線程池)來管理這些線程。當請求到來時,可以從池中去 一個線程出來,執行任務(通常是對請求的響應),當任務結束后,再將這個線程放入池中備用;如果請求到來而池中沒有空閑的線程,該請求需要排隊等候。最后,當服務關閉 時銷毀該池即可。 jdk提供線程池,在java.util.concurrent包內,可以看下jdk關于線程池的實現,以下關于線程池的文章可以看看: http://www.oschina.net/question/565065_86540 http://m.survivalescaperooms.com/dolphin0520/p/3932921.html

http://www.oschina.net/question/565065_86540

http://zmx.VEvb.com/blog/2161831

線程池的關鍵點是:1、盡量減少線程切換和管理的開支; 2、最大化利用cpu。 對于1,要求線程數盡量少,這樣可以減少線程切換和管理的開支; 對于2,要求盡量多的線程,以保證CPU資源最大化的利用。 所以對于任務耗時短的情況,要求線程盡量少,如果線程太多,有可能出現線程切換和管理的時間,大于任務執行的時間,那效率就低了; 對于耗時長的任務,要分是cpu任務,還是io等類型的任務。如果是cpu類型的任務,線程數不宜太多;但是如果是io類型的任務,線程多一些更好,可以更充分利用cpu。 所以: 高并發,低耗時的情況:建議少線程,只要滿足并發即可;例如并發100,線程池可能設置為10就可以 低并發,高耗時的情況:建議多線程,保證有空閑線程,接受新的任務;例如并發10,線程池可能就要設置為20; 高并發高耗時:1要分析任務類型,2增加排隊,3、加大線程數


6、使用管道在線程間流動數據

  • Java提供了各種各樣的輸入輸出流(stream),使程序員能夠很方便地對數據進行操作。其中,管道(pipe)流是一種特殊的流,用于在不同線程間直接傳送數據。一個線程發送數據到輸出管道,另一個線程從輸入管道中讀出數據。通過使用管道,達到實現多個線程間通信的目的。那么,如何創建和使用管道呢?
  • Java提供了兩個特殊的專門用來處理管道的類,它們就是PipedInputStream類和PipedOutputStream類。
  • PipedInputStream代表了數據在管道中的輸出端,也就是線程從管道讀出數據的一端;PipedOutputStream代表了數據在管道中的輸入端,也就是線程向管道寫入數據的一端,這兩個類一起使用就可以創建出數據輸入輸出的管道流對象。
  • 一旦創建了管道之后,就可以利用多線程的通信機制對磁盤中的文件通過管道進行數據的讀寫,從而使多線程的程序設計在實際應用中發揮更大的作用。

可以看看以下文章: http://www.zhujiangroad.com/PRogram/JAVA/1942.html


7、stop、suspend、resume方法為什么被淘汰,以及安全實現相似行為的方式

  • suspend()方法就是將一個線程掛起(暫停),resume()方法就是將一個掛起線程復活繼續執行。
  • suspend() 和 resume() 方法:兩個方法配套使用,suspend()使得線程進入阻塞狀態,并且不會自動恢復,必須其對應的 resume() 被調用,才能使得線程重新進入可執行狀態。典型地,suspend() 和 resume() 被用在等待另一個線程產生的結果的情 形:測試發現結果還沒有產生后,讓線程阻塞,另一個線程產生了結果后,調用 resume() 使其恢復。如果調用suspend的方法線程試圖取得相同的鎖,程序就會死鎖。
  • 最謹慎的方法是讓“目標”線程輪詢一個指示所需線程狀態(活動或掛起)的變量。當需要掛起狀態時,線程將使用 Object.wait 進行等待。當線程恢復時,將使用 Object.notify 通知目標線程。
  • stop這個方法將終止所有未結束的方法,包括run方法。當一個線程停止時候,他會立即釋放所有他鎖住對象上的鎖。這會導致對象處于不一致的狀態。假如一個方法在將錢從一個賬戶轉移到另一個賬戶的過程中,在取款之后存款之前就停止了。那么現在銀行對象就被破壞了。因為鎖已經被釋放了。當線程想終止另一個線程的時候,它無法知道何時調用stop是安全的,何時會導致對象被破壞。所以這個方法被棄用了。你應該中斷一個線程而不是停止他。我們平常使用windows系統,某個程序會出現無法響應,此時在任務管理器中直接終止掉進程,像類似的情況,是可以直接使用stop的。
  • 大多數使用 stop 的情況都應該用簡單修改一些變量以指示目標線程應停止運行的代碼所代替。目標線程應該定期檢查該變量,并在該變量指示需要它停止運行時以一種合理的方法從其 run 方法中返回(這是 JavaSoft 教程始終推薦的方法)。要確保停止請求的即時通訊,該變量必須是 volatile(迅變)的(或對該變量的訪問必須是同步的)。

可以看看以下文章: http://blog.csdn.net/linchengzhi/article/details/7468395 http://blog.163.com/feng_welcome/blog/static/17177032420112246191360/


8、interrupt方法:"完美終止線程"

  • Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,在線程受到阻塞時拋出一個中斷信號,這樣線程就得以退出阻塞的狀態。更確切的說,如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,那么,它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態。
  • 因此,如果線程被上述幾種方法阻塞,正確的停止線程方式是設置共享變量,并調用interrupt()(注意變量應該先設置)。如果線程沒有被阻塞,這時調用interrupt()將不起作用;否則,線程就將得到異常(該線程必須事先預備好處理此狀況),接著逃離阻塞狀態。在任何一種情況中,最后線程都將檢查共享變量然后再停止。

9、死鎖

  • 死鎖產生原因

Java線程死鎖是一個經典的多線程問題,因為不同的線程都在等待那些根本不可能被釋放的鎖,從而導致所有的工作都無法完成。假設有兩個線程,分別代表兩個饑餓的 人,他們必須共享刀叉并輪流吃飯。他們都需要獲得兩個鎖:共享刀和共享叉的鎖。假如線程 “A”獲得了刀,而線程“B”獲得了叉。線程“A”就會進入阻塞狀態來等待獲得叉,而 線程“B”則阻塞來等待“A”所擁有的刀。 導致死鎖的根源在于不適當地運用“synchronized”關鍵詞來管理線程對特定對象的訪問 避免死鎖的一個通用的經驗法則是:當幾個線程都要訪問共享資源A、B、C時,保證使每個線程都按照同樣的順序去訪問它們,比如都先訪問A,在訪問B和C。 可以看看以下文章: http://leowzy.VEvb.com/blog/740859

  • 查看線程死鎖

http://m.survivalescaperooms.com/ilahsa/archive/2013/06/03/3115410.html


10、自運行活動類技術,推薦使用在匿名內部類中隱藏run方法的設計方案

  • 自運行活動類技術:在Thread子類或實現runnable接口類中的構造函數中啟動線程;
  • 隱藏run方法的設計方案:在普通類的構造方法中使用匿名內部類創建線程內,再啟動線程

學習的時候敲的一些小例子,有興趣可以看看,地址:http://yunpan.cn/cwJMuWEntQNN2 訪問密碼 f1ca


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 杭锦后旗| 昆山市| 那坡县| 梁山县| 嘉义县| 景宁| 汤阴县| 青冈县| 台北县| 兖州市| 宣化县| 伊春市| 郸城县| 上蔡县| 木里| 宁陵县| 千阳县| 蓬溪县| 林州市| 汽车| 巩留县| 平陆县| 芮城县| 田东县| 隆德县| 登封市| 东乡族自治县| 上思县| 杂多县| 鄂尔多斯市| 蒲江县| 新和县| 清徐县| 平定县| 琼结县| 陈巴尔虎旗| 锦屏县| 苏尼特左旗| 哈尔滨市| 怀宁县| 慈利县|