淺析Java并發(fā)編程(一)基礎(chǔ)概念&理論

前言

學(xué)習(xí)Java并發(fā)編程,首先要搞清楚一些基礎(chǔ)概念與理論,這有助于進一步的理解并發(fā)編程和寫出正確有效的并發(fā)代碼。本文是作者自己對Java并發(fā)編程的一些基礎(chǔ)概念與理論的理解與總結(jié),不對之處狭姨,望指出,共勉苏遥。

進程與線程

簡而言之饼拍,進程是操作系統(tǒng)進行資源分配的基本單位,而線程是操作系統(tǒng)進行調(diào)度的基本單位田炭,所有線程共享進程所擁有的資源师抄。

推薦閱讀:進程和線程之由來

為什么需要并發(fā)

CPU的處理速度越來越快,核心越來越多教硫,但IO的速度相對CPU來說非常的緩慢(就像自行車與火箭)叨吮。CPU通過IO獲取數(shù)據(jù)進行計算時經(jīng)常需要等待,導(dǎo)致利用率很低瞬矩,不能發(fā)揮自身速度的優(yōu)勢(就像一名程序員茶鉴,一天可以完成一百個需求,公司卻只給其配備一名產(chǎn)品經(jīng)理景用,每天提一個需求)涵叮。而并發(fā)則可以更好的利用CPU,CPU同時為多個線程提供計算伞插,當(dāng)其中一個線程因IO等待時迅速切換至其他進程或線程(就像公司為這名程序員配備多名產(chǎn)品經(jīng)理割粮,不停的提需求,程序員不停的切換項目編寫代碼媚污,充分壓榨其勞動力)舀瓢。

從另一種角度來說,并發(fā)其實是一種解耦合的策略耗美,它幫助我們把做什么(目標)和什么時候做(時機)分開京髓。這樣做可以明顯改進應(yīng)用程序的吞吐量(獲得更多的CPU調(diào)度時間)和結(jié)構(gòu)(程序有多個部分在協(xié)同工作)航缀。

并發(fā)編程的優(yōu)勢

  • 充分利用CPU(核心)的計算能力
  • 提高程序的吞吐量,改善性能

并發(fā)編程的劣勢

  • 并發(fā)在CPU有很多空閑時間時能明顯改進程序的性能朵锣,但當(dāng)線程數(shù)量較多的時候,線程間頻繁的調(diào)度切換反而會讓系統(tǒng)的性能下降
  • 編寫正確的并發(fā)程序是非常復(fù)雜的甸私,即使對于很簡單的問題
  • 測試并發(fā)程序是困難的诚些,并發(fā)程序中的缺陷通常不易重現(xiàn)也不容易被發(fā)現(xiàn)

Java內(nèi)存模型

JMM

Java內(nèi)存模型(Java Memory Model,JMM) 是對Java并發(fā)編程中線程與內(nèi)存的關(guān)系的定義皇型,即線程間的共享變量存儲在主內(nèi)存(Main Memory) 中诬烹,每個線程都有一個私有的本地工作內(nèi)存(Local Memory),線程的本地內(nèi)存中存儲了該線程使用到的共享變量的副本(從主內(nèi)存復(fù)制而來)弃鸦,線程對該變量的所有讀/寫操作都必須在自己的本地內(nèi)存中進行绞吁,不同的線程之間也無法直接訪問對方本地內(nèi)存中的變量,線程間變量值的傳遞需要通過與主內(nèi)存同步來完成唬格。理解Java內(nèi)存模型家破,對于編寫正確的Java并發(fā)程序來說至關(guān)重要。

all threads share the main memory. each thread uses a local working memory. refreshing local memory to/from main memory must comply to JMM rules.

推薦閱讀:The Java Language Specification 17.4. Memory Model 购岗、 淺析JVM(二)運行時數(shù)據(jù)區(qū) 汰聋、深入理解Java內(nèi)存模型

Java并發(fā)編程需要考慮的問題

  • 共享性

由Java內(nèi)存模型得知,共享變量是所有線程共享的喊积,如果多個線程對共享變量同時進行讀/寫操作程序可能會達不到預(yù)期的結(jié)果烹困。當(dāng)然,如果每個線程操作的始終是各自本地工作內(nèi)存中的變量則不存在共享性問題乾吻,比如通過方法參數(shù)傳入髓梅、使用局部變量、創(chuàng)建新的實例绎签。有過Java Web開發(fā)經(jīng)驗的人都知道枯饿,Servlet就是以單實例多線程的方式工作,和每個請求相關(guān)的數(shù)據(jù)都是通過Servlet的service方法(或者是doGetdoPost方法)的參數(shù)傳入的诡必。只要Servlet中的代碼只使用局部變量鸭你,Servlet就不會導(dǎo)致同步問題。Spring MVC的控制器也是這么做的擒权,從請求中獲得的對象都是以方法的參數(shù)傳入而不是作為類的成員袱巨,很明顯Struts 2的做法就正好相反,因此Struts 2中作為控制器的Action類都是每個請求對應(yīng)一個實例碳抄。

  • 互斥性

互斥性指的是同一時間只允許一個線程對共享變量進行操作愉老,以保證線程安全,具有唯一性和排它性剖效。在Java 中通常用鎖來保證共享變量的互斥性嫉入,為了提高效率通常允許多個線程同時對共享變量進行讀操作焰盗,但同一時間內(nèi)只允許一個線程對其進行寫操作,所以鎖又分為共享鎖和排它鎖咒林,也叫做讀鎖和寫鎖熬拒。對于使用不變模式(被 final 修飾)的“變量”,則無需關(guān)心互斥性垫竞,因為其只允許線程對其進行讀操作澎粟。(不變模式也是Java并發(fā)編程時可以考慮的一種設(shè)計。讓對象的狀態(tài)是不變的欢瞪,如果希望修改對象的狀態(tài)活烙,就會創(chuàng)建對象的副本并將改變寫入副本而不改變原來的對象,這樣就不會出現(xiàn)狀態(tài)不一致的情況遣鼓,因此不變對象是線程安全的啸盏。Java中我們使用頻率極高的String類就采用了這樣的設(shè)計)

  • 原子性

原子性指的是對共享變量的操作是一個獨立的、不可分割的整體骑祟。換句話說回懦,就是一次操作,是一個連續(xù)不可中斷的過程次企,共享變量的值不會執(zhí)行到一半的時候被其他線程所修改粉怕。比如,我們經(jīng)常使用的整數(shù)i++ 的操作抒巢,其實需要分成三個步驟:(1)讀取整數(shù)i 的值贫贝;(2)對i進行加一操作;(3)將結(jié)果寫回主內(nèi)存蛉谜。在多線程下該操作便會出現(xiàn)原子性問題稚晚,不能獲得預(yù)期的值。

  • 可見性

可見性指的是當(dāng)一個線程對共享變量進行更改后型诚,其他線程對更改后的值是可見的(立即對主內(nèi)存進行同步)客燕。Java提供了volatile關(guān)鍵字來保證可見性。當(dāng)一個共享變量被volatile修飾時狰贯,它會保證線程工作內(nèi)存中修改的值會立即被更新到主內(nèi)存中也搓,其他線程也會將主內(nèi)存中的新值同步至工作內(nèi)存,需要注意的是volatile并不能保證原子性涵紊。

如上圖所示傍妒,如果可見性得到保證,那么當(dāng)線程1將X的值更改為2時摸柄,線程2內(nèi)的X值也將同步為2颤练,否則線程2內(nèi)的X值仍為1。

  • 有序性

為了提高性能驱负,編譯器和處理器可能會對指令做重排序嗦玖,重排序通郴脊停可以分為下面三種

  1. 編譯級別的重排序,比如編譯器的優(yōu)化
  2. 指令級重排序宇挫,比如CPU指令執(zhí)行的重排序
  3. 內(nèi)存系統(tǒng)的重排序苛吱,比如緩存和讀寫緩沖區(qū)導(dǎo)致的重排序

有序性指的就是在多線程并發(fā)的情況下,代碼實際執(zhí)行的順序器瘪、結(jié)果和單線程是一樣的翠储,不會因為重排序的問題導(dǎo)致結(jié)果不可預(yù)知。

<small>注:水平有限娱局,可能理解的不夠透徹彰亥,有興趣的可以看看 Doug LeaSynchronization and the Java Memory Model咧七,我想沒有誰比他理解的更透徹了衰齐。</small>

Java中創(chuàng)建線程的方式

  • 方式一,繼承java.lang.Thread
 public static void method1() {
        class Task extends Thread {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Started");
            }
        }

        new Task().start();
    }
  • 方式二继阻,實現(xiàn)java.lang.Runnable接口(推薦)
public static void method2() {
        class Task implements Runnable {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Started");
            }
        }
        new Thread(new Task()).start();
    }
  • 方式三耻涛,實現(xiàn)java.util.concurrent.Callable接口(推薦)
public static void method3() {
        class Task implements Callable {

            @Override
            public Object call() throws Exception {
                System.out.println(Thread.currentThread().getName() + " Started");
                //求和
                return 1 + 1;
            }
        }

        ExecutorService es = Executors.newFixedThreadPool(1);
        Future future = es.submit(new Task());
        try {
            System.out.println("Calculate Completed Sum:" + future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        es.shutdown();
    }

查看該部分源碼

參考


查看《淺析Java并發(fā)編程》系列文章目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瘟檩,隨后出現(xiàn)的幾起案子抹缕,更是在濱河造成了極大的恐慌,老刑警劉巖墨辛,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卓研,死亡現(xiàn)場離奇詭異,居然都是意外死亡睹簇,警方通過查閱死者的電腦和手機奏赘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來太惠,“玉大人磨淌,你說我怎么就攤上這事≡湓ǎ” “怎么了梁只?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長埃脏。 經(jīng)常有香客問我搪锣,道長,這世上最難降的妖魔是什么彩掐? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任淤翔,我火速辦了婚禮,結(jié)果婚禮上佩谷,老公的妹妹穿的比我還像新娘旁壮。我一直安慰自己监嗜,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布抡谐。 她就那樣靜靜地躺著裁奇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪麦撵。 梳的紋絲不亂的頭發(fā)上刽肠,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音免胃,去河邊找鬼音五。 笑死,一個胖子當(dāng)著我的面吹牛羔沙,可吹牛的內(nèi)容都是我干的躺涝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扼雏,長吁一口氣:“原來是場噩夢啊……” “哼坚嗜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起诗充,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤苍蔬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蝴蜓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碟绑,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年茎匠,在試婚紗的時候發(fā)現(xiàn)自己被綠了格仲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡汽抚,死狀恐怖抓狭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情造烁,我是刑警寧澤否过,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站惭蟋,受9級特大地震影響苗桂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜告组,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一煤伟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦便锨、人聲如沸围辙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姚建。三九已至,卻和暖如春吱殉,著一層夾襖步出監(jiān)牢的瞬間掸冤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工友雳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留稿湿,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓押赊,卻偏偏與公主長得像饺藤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子考杉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容