原創(chuàng)|行業(yè)資訊|編輯:龔雪|2014-05-22 09:40:35.000|閱讀 391 次
概述:介紹了解ava并發(fā)編程原理,工作狀態(tài),模塊模式等,從而快速掌握J(rèn)ava并發(fā)編程,并附上各種資源工具。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
Java作為最流行的編程語(yǔ)言之一,隨著 Java 8的到來(lái),越來(lái)越多的人開(kāi)始學(xué)習(xí),并深入研究!下面將介紹 Java并發(fā)編程,讓開(kāi)發(fā)者在最短的時(shí)間里掌握并發(fā)編程。
1.1. 什么是并發(fā)?
并 發(fā)是一種能并行運(yùn)行多個(gè)程序或并行運(yùn)行一個(gè)程序中多個(gè)部分的能力。如果程序中一個(gè)耗時(shí)的任務(wù)能以異步或并行的方式運(yùn)行,那么整個(gè)程序的吞吐量和可 交互性將大大改善。現(xiàn)代的PC都有多個(gè)CPU或一個(gè)CPU中有多個(gè)核。是否能合理運(yùn)用多核的能力將成為一個(gè)大規(guī)模應(yīng)用程序的關(guān)鍵。
1.2. 進(jìn)程 vs 線程
進(jìn)程是以獨(dú)立于其他進(jìn)程的方式運(yùn)行的,進(jìn)程間是互相隔離的。一個(gè)進(jìn)程無(wú)法直接訪問(wèn)另一個(gè)進(jìn)程的數(shù)據(jù)。進(jìn)程的資源諸如內(nèi)存和CPU時(shí)間片都是由操作系統(tǒng)來(lái)分配。
線程又被稱(chēng)為輕量級(jí)進(jìn)程。每個(gè)線程有它獨(dú)自的調(diào)用棧, 但是在同一進(jìn)程下的線程又能互相訪問(wèn)它們間的共享數(shù)據(jù)。每個(gè)線程都有它獨(dú)自的緩存。如果一個(gè)線程讀取了某些共享數(shù)據(jù),那么它將這些數(shù)據(jù)存放在自己的緩存中以供將來(lái)再次讀取。
一個(gè) Java應(yīng)用程序默認(rèn)以一個(gè)進(jìn)程的形式運(yùn)行著。在一個(gè) Java程序中,你將協(xié)同多個(gè)不同的線程一起完成并行運(yùn)算或?qū)崿F(xiàn)異步行為。
2.1. 并發(fā)性能的上限
之所以并發(fā)號(hào)稱(chēng)能較快的完成某些任務(wù)是因?yàn)檫@些任務(wù)能被分組成多個(gè)子任務(wù),并且這些子任務(wù)能被并行的執(zhí)行。所以一個(gè)任務(wù)的實(shí)際運(yùn)行效率將受限于該任務(wù)中能并行執(zhí)行的部分。
一個(gè)程序理論上可達(dá)到的最高并發(fā)性能可通過(guò)以下一個(gè)被稱(chēng)為Amdahl 定律來(lái)計(jì)算出:
設(shè)F為一個(gè)程序中不能被并行執(zhí)行的百分比,N是處理器的數(shù)量,那么理論上該程序能獲得的最高并發(fā)性能將可能是1/ (F+ ((1-F)/n)).
2.2. 并發(fā)問(wèn)題
線程有獨(dú)自的調(diào)用棧,但是又能互相訪問(wèn)共享的數(shù)據(jù)。所以這里你會(huì)遇到兩個(gè)問(wèn)題,可見(jiàn)性和訪問(wèn)。
可見(jiàn)性問(wèn)題發(fā)生于如果線程A先讀取了某些共享數(shù)據(jù),之后線程B對(duì)這些數(shù)據(jù)進(jìn)行了修改,那么線程A可能看不到線程B對(duì)這數(shù)據(jù)的改動(dòng)。
訪問(wèn)問(wèn)題發(fā)生于于多個(gè)線程同時(shí)訪問(wèn)修改同一個(gè)共享數(shù)據(jù)。可見(jiàn)性及訪問(wèn)問(wèn)題將導(dǎo)致:
活躍性失敗——由于并發(fā)訪問(wèn)數(shù)據(jù)導(dǎo)致程序無(wú)任何反應(yīng)。 譬如,死鎖。
安全性失敗——程序創(chuàng)建了錯(cuò)誤的數(shù)據(jù)。
3.1. 進(jìn)程與線程
一個(gè) Java程序默認(rèn)以一個(gè)線程運(yùn)行在自己的進(jìn)程中。 Java語(yǔ)言通過(guò)Thread相關(guān)代碼來(lái)支持線程。 Java程序可通過(guò)Thread這個(gè)類(lèi)來(lái)創(chuàng)建線程。從 Java1.5起,在 Java.util.concurrent中提供了改進(jìn)的并發(fā)庫(kù)。
3.2. 鎖和線程同步
Java提供了“鎖” 機(jī)制來(lái)保護(hù)代碼片段免于被多個(gè)線程在同一時(shí)刻運(yùn)行。最簡(jiǎn)單的鎖住一個(gè)方法或一個(gè)類(lèi)就是在該方法或類(lèi)前添加synchronized關(guān)鍵字
保證了在Java中synchronized關(guān)鍵字保證了在同一時(shí)刻,只有單個(gè)線程能訪問(wèn)這塊代碼,每個(gè)進(jìn)入這同步代碼塊的線程都將能看到之前持有相同鎖進(jìn)入的線程的所做的改動(dòng).對(duì)于線程間的可靠通訊及互斥訪問(wèn) 來(lái)說(shuō),同步是非常必要的
你可以在定義方法時(shí)使用synchronized關(guān)鍵字。這個(gè)關(guān)鍵字保證了同一時(shí)刻只有一個(gè)線程能運(yùn)行這個(gè)方法。其他同樣調(diào)用了這個(gè)方法的線程將被阻塞直到第一個(gè)線程離開(kāi)這個(gè)方法。
1 public synchronized void critial() { 2 // some thread critical stuff 3 // here 4 }
你同樣也能用synchronized關(guān)鍵字來(lái)保護(hù)方法中的代碼塊。這塊代碼將由一個(gè)關(guān)鍵對(duì)象來(lái)保護(hù),該關(guān)鍵對(duì)象可以是個(gè)string或其他object。這個(gè)關(guān)鍵對(duì)象就被稱(chēng)為 lock。所有被相同lock保護(hù)的代碼在同一時(shí)刻只能被單個(gè)線程執(zhí)行。
舉例來(lái)說(shuō),以下的數(shù)據(jù)結(jié)構(gòu)將保證同時(shí)只有單個(gè)線程可以訪問(wèn)add和next方法:
01 package de.vogella.pagerank.crawler; 02 03 import Java.util.ArrayList; 04 import Java.util.List; 05 /*** Data structure for a web crawler. Keeps track of the visited sites and keeps 06 * a list of sites which needs still to be crawled. 07 * @author Lars Vogel 08 */ 09 public class CrawledSites { 10 private List<String> crawledSites = new ArrayList<String>(); 11 private List<String> linkedSites = new ArrayList<String>(); 12 13 public void add(String site) { 14 synchronized (this) { 15 if (!crawledSites.contains(site)) { 16 linkedSites.add(site); 17 } 18 } 19 } 20 /** * Get next site to crawl. Can return null (if nothing to crawl) */ 21 public String next() { 22 if (linkedSites.size() == 0) { 23 return null; 24 } 25 synchronized (this) { 26 <em>// Need to check again if size has changed</em> 27 if (linkedSites.size() > 0) { 28 String s = linkedSites.get(0); 29 linkedSites.remove(0); 30 crawledSites.add(s); 31 return s; 32 } 33 return null; 34 } 35 } 36 37 }
3.3. Volatile
如果一個(gè)變量聲明時(shí)使用了volatile 關(guān)鍵字,那么該關(guān)鍵字保證了,任何讀取該變量的線程都將讀到最新寫(xiě)進(jìn)該變量的值。但volatile 關(guān)鍵字不會(huì)保證變量上的任何互斥訪問(wèn)。
因?yàn)樵?nbsp;Java 5中,對(duì)一個(gè)聲明了volatile 的變量進(jìn)行寫(xiě)操作會(huì)導(dǎo)致該寫(xiě)操作所在該線程之前所有的對(duì)非volatile 變量的修改進(jìn)行同步更新。這也可用來(lái)更新引用類(lèi)型變量。例如,有個(gè)volatile 類(lèi)型的變量person。你必須創(chuàng)建一個(gè)臨時(shí)變量person,然后調(diào)用SETTER方法來(lái)初始化這變量然后將臨時(shí)變量賦值給final變量。 這會(huì)使這個(gè)變量的地址發(fā)生改變并且此變量的值對(duì)于其他線程變?yōu)榭梢?jiàn)。
4.1. 概覽
Java內(nèi)存模型描述了線程內(nèi)存與主存間的通訊關(guān)系。
Java內(nèi)存模型定義了線程內(nèi)的內(nèi)存改變將怎樣傳遞到其他線程的規(guī)則,同樣也定義了線程內(nèi)存與主存進(jìn)行同步的場(chǎng)景,也描述了哪些操作屬于原子操作及操作間的順序。
4.2. 原子操作
一個(gè)原子操作是指一個(gè)執(zhí)行時(shí)不會(huì)被其他操作影響到的最小單位的操作。
Java語(yǔ)言規(guī)范保證了對(duì)一個(gè)變量的讀和寫(xiě)操作都是原子的(除了LONG和DOUBLE類(lèi)型的變量)。對(duì)于LONG或DOUBLE類(lèi)型的變量,只有當(dāng)這些變量聲明時(shí)使用了volatile關(guān)鍵字才是原子的
假設(shè)定義了一個(gè)INT變量I,那么在 Java中,I++操作不是一個(gè)原子操作。同樣,這對(duì)于其他數(shù)字類(lèi)型的變量也都不是一個(gè)原子操作。
I++操作先從I中讀取了當(dāng)前值(這是個(gè)原子操作)然后再讓它加上1寫(xiě)回(原子操作)。但是在讀和寫(xiě)這兩個(gè)操作間,I的值有可能被改變。
從 Java1.5起, Java提供了原子變量,例如AtomicInteger 或 AtomicLong 都提供了類(lèi)似 getAndDecrement(), getAndIncrement() 及 getAndSet()等原子方法。
4.3.同步代碼塊的內(nèi)存更新
Java內(nèi)存模型保證了每個(gè)進(jìn)入相同鎖同步塊的線程都能看到之前進(jìn)入的其他所有線程修改的結(jié)果。
溫馨小提示:好的資源,在 Java開(kāi)發(fā)中能事半功倍!
Java并發(fā)編程基礎(chǔ)://ifeve.com/java-concurrency-cookbook/
業(yè)界被公認(rèn)為最好的Java開(kāi)發(fā)平臺(tái):IntelliJ IDEA
最實(shí)惠、綜合全面的J2EE IDE與Web開(kāi)發(fā)工具套件:MyEclipse
多平臺(tái)Java安裝文件生成工具:install4j
全面測(cè)試Java程序的工具:Parasoft Jtest
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn