Java多線程總結(jié)

多線程基礎(chǔ)概念介紹

進程
是程序或任務(wù)的執(zhí)行的過程,具有動態(tài)性奋隶;
它持有資源(共享內(nèi)存,共享文件)和線程悦荒;
如:打開QQ、搜狗輸入法軟件時嘹吨,我們啟動了兩個進程搬味。

線程
系統(tǒng)中最小的執(zhí)行單元。 比如一個軟件里邊的各種任務(wù)就是線程;
同一進程中有多個線程碰纬;
線程共享進程的資源萍聊。

多線程
一個進程中可以開啟多條線程,多條線程可以并行(同時)執(zhí)行不同的任務(wù)悦析;
多線程技術(shù)可以提高程序的執(zhí)行效率寿桨。

線程創(chuàng)建的兩種方式比較

  • 繼承Thread類
public class ExtendThread {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
//      mt.start();
        MyThread mt2 = new MyThread();
        mt2.start();
    }

}

class MyThread extends Thread{
    
    @Override
    public void run(){
        System.out.println("使用extends創(chuàng)建線程!!");
    }
}

  • 實現(xiàn)Runnable接口
public class ImplementThread {
    public static void main(String[] args) {
        Mythread2 mt = new Mythread2();
        Thread th = new Thread(mt);
        th.start();
        Thread th2 = new Thread(mt);
        th2.start();
    }
}

class Mythread2 implements Runnable{

    @Override
    public void run() {
        System.out.println("使用implements創(chuàng)建線程强戴!");
    }
    
}

注意事項:同一線程的start()方法不能重復(fù)調(diào)用
start()方法相關(guān)源碼如下:

public synchronized void start() {
        if (threadStatus != 0)  //start()方法不能被重復(fù)調(diào)用亭螟,否則會拋出異常
            throw new IllegalThreadStateException();
        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);  //加入到線程組中
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
}
private native void start0();

附:Thread常用方法介紹

Thread常用方法

兩種實現(xiàn)方式對比:

  • Runnable方式可以避免Thread方式由于Java單繼承特性帶來的缺陷

  • Runnable方式實現(xiàn)可以被多個線程共享,適合于多個線程處理同一資源的情況

線程的生命周期

下圖是線程生命周期的詳細(xì)狀態(tài)轉(zhuǎn)換圖(注意:不能從阻塞狀態(tài)直接到運行狀態(tài))


線程的生命周期

下面將對每一個狀態(tài)進行簡單的介紹

  • 創(chuàng)建
    新建一個線程對象骑歹,如Thread td = new Thread();

  • 就緒
    創(chuàng)建線程對象之后预烙,調(diào)用start()方法(注意:此時線程只是進入了線程隊列,等待獲取CPU服務(wù)道媚,具備了運行條件扁掸,但不一定已經(jīng)開始運行了

  • 運行
    處于就緒狀態(tài)的線程,一旦獲取CPU資源最域,就會進入運行狀態(tài)谴分,開始執(zhí)行run()方法里面的邏輯

  • 阻塞
    一個正在執(zhí)行的線程在某些情況下,由于某種原因而暫時讓出了CPU資源镀脂,暫停了自己的執(zhí)行牺蹄,便進入了阻塞狀態(tài)。如:調(diào)用了sleep()方法

  • 終止
    線程的run()方法執(zhí)行完畢狗热,或者線程調(diào)用了stop()方法(現(xiàn)在已經(jīng)不推薦使用這種方式)钞馁,線程便進入了終止?fàn)顟B(tài)

附:更加詳細(xì)的線程狀態(tài)轉(zhuǎn)換圖

更加詳細(xì)的線程狀態(tài)轉(zhuǎn)換圖

守護線程介紹

Java線程有兩類

  • 用戶線程
    運行在前臺,執(zhí)行具體的任務(wù)匿刮。如:用戶的主線程僧凰,連接網(wǎng)絡(luò)的子線程等

  • 守護線程
    運行在后臺,為其他前臺線程服務(wù)熟丸。
    特點:
    一旦所有用戶線程都結(jié)束運行训措,守護線程也會隨JVM一起結(jié)束工作
    應(yīng)用:
    數(shù)據(jù)庫連接池中的監(jiān)測線程
    JVM虛擬機啟動后的監(jiān)測線程
    常見:
    垃圾回收線程
    使用方法:
    調(diào)用Thread類的setDaemon(true)方法來設(shè)置當(dāng)前線程為守護線程
    注意事項:
    ①setDaemon(true)必須在start()方法之前調(diào)用,否則會拋出異常IllegalThreadStateException
    ②在守護線程中產(chǎn)生的新線程也是守護線程
    ③不是所有的任務(wù)都可已分配給守護線程來執(zhí)行光羞,如:讀寫操作或計算邏輯

內(nèi)存可見性

原子性
跟數(shù)據(jù)庫事務(wù)的原子性概念差不多绩鸣,即一個操作(有可能包含有多個子操作)要么全部執(zhí)行(生效),要么全部都不執(zhí)行(都不生效)纱兑。

可見性
一個線程對共享變量值的修改呀闻,能夠及時地被其他線程看到。

共享變量
如果一個變量在多個線程的工作內(nèi)存中都存在副本潜慎,那么這個變量就是這幾個線程的共享變量捡多。

Java內(nèi)存模型(JMM)
描述了Java程序中各種變量(線程共享變量)的訪問規(guī)則蓖康,以及在JVM中將變量存儲到內(nèi)存和從內(nèi)存中讀取出變量這樣的底層細(xì)節(jié)。

Java內(nèi)存模型

注意:

  • 所有的變量都存儲在主內(nèi)存中
  • 每個線程都有自己獨立的工作內(nèi)存垒手,里面保存該線程使用到的變量的副本(即主內(nèi)存中共享變量的副本)

共享變量可見性實現(xiàn)原理

共享變量可見性實現(xiàn)原理

線程1對共享變量的修改想要被線程2及時看到蒜焊,必須經(jīng)過以下兩個步驟:

  • 把工作內(nèi)存1中更新過的共享變量刷新到主內(nèi)存中
  • 將主內(nèi)存中最新的共享變量的值更新到工作內(nèi)存2中

Java語言層面實現(xiàn)可見性的兩種方式

  • synchronized
    synchronized 關(guān)鍵字,代表這個方法加鎖,相當(dāng)于不管哪一個線程A每次運行到這個方法時,都要檢查有沒有其它正在用這個方法的線程B(或者C D等),有的話要等正在使用這個方法的線程B(或者C D)運行完這個方法后再運行此線程A科贬,如果沒有其他線程正在用這個方法則線程A可以運行這個方法泳梆。

線程執(zhí)行互斥代碼的過程:
①獲得互斥鎖
②清空工作內(nèi)存
③從主內(nèi)存拷貝變量的最新副本到工作內(nèi)存
④執(zhí)行代碼
⑤將更改后的共享變量的值刷新到主內(nèi)存
⑥釋放互斥鎖

synchronized 關(guān)鍵字的兩種用法
synchronized 方法
通過在方法聲明中加入 synchronized關(guān)鍵字來聲明 synchronized 方法。如:

public synchronized void accessVal(int newVal);

synchronized 塊
通過 synchronized關(guān)鍵字來聲明synchronized 塊榜掌。語法如下:

synchronized(syncObject)
{  
      //允許訪問控制的代碼
}

對synchronized (this)的一些理解(this指的是調(diào)用這個方法的對象)

  1. 當(dāng)兩個并發(fā)線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時优妙,一個時間內(nèi)只能有一個線程得到執(zhí)行。另一個線程必須等待當(dāng)前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊唐责。
  2. 然而鳞溉,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊鼠哥。
  3. 尤其關(guān)鍵的是熟菲,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞朴恳。
  4. 第三個例子同樣適用其它同步代碼塊抄罕。也就是說,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時于颖,它就獲得了這個object的對象鎖呆贿。結(jié)果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞森渐。
  5. 以上規(guī)則對其它對象鎖同樣適用做入。

注意:
線程加鎖時,將清空工作內(nèi)存中共享變量的值同衣,從而使用共享變量時需要從主內(nèi)存重新讀取最新的值
線程解鎖時竟块,必須把共享變量的最新值刷新到主內(nèi)存中

  • volatile
    能夠保證volatile變量的可見性;不能保證volatile變量復(fù)合操作的原子性

實現(xiàn)方式:
通過加入內(nèi)存屏障和禁止重排序優(yōu)化實現(xiàn)
①對volatile變量執(zhí)行寫操作時耐齐,會在寫操作后加入一條store屏障指令
②對volatile變量執(zhí)行讀操作時浪秘,會在讀操作前加入一條load屏障指令

線程寫volatile變量的過程
①改變線程工作內(nèi)存中volatile變量副本的值
②將改變后的副本的值從工作內(nèi)存刷新到主內(nèi)存
線程讀volatile變量的過程:
①從主內(nèi)存中讀取volatile變量的最新值到線程的工作內(nèi)存中
②從工作內(nèi)存中讀取volatile變量的副本的值

適用場合:
①對變量的寫操作不依賴其當(dāng)前值
不滿足:number++、count = count+5
滿足:boolean變量埠况,記錄溫度變化的變量

②變量沒有包含在具有其它變量的不變式中
不滿足:不變式 low < up

兩種實現(xiàn)方式比較

  • volatile不用加鎖耸携,比synchronized更加輕量級,不會阻塞線程辕翰;
  • 從內(nèi)存可見性分析夺衍,volatile讀操作相當(dāng)于加鎖,volatile寫操作相當(dāng)于解鎖喜命;
  • synchronized既能保證可見性沟沙,又能夠保證原子性的畴;而volatile只能保證可見性,不能夠保證原子性

參考資料

[1]Java總結(jié)篇系列:Java多線程(一)
[2]深入理解java中的synchronized關(guān)鍵字
[3]java中synchronized的用法詳解

補充問題

1.Thread類的yield方法作用
yield()應(yīng)該做的是讓當(dāng)前運行線程回到可運行狀態(tài)尝胆,只允許具有相同優(yōu)先級的其他線程獲得運行機會。因此护桦,使用yield()的目的是讓相同優(yōu)先級的線程之間能適當(dāng)?shù)妮嗈D(zhuǎn)執(zhí)行含衔。但是,實際中無法保證yield()達到讓步目的二庵,因為讓步的線程還有可能被線程調(diào)度程序再次選中贪染。

注意:sleep()可以使低優(yōu)先級的線程得到執(zhí)行的機會,當(dāng)然也可以讓同優(yōu)先級催享、高優(yōu)先級的線程有執(zhí)行的機會杭隙。而yield()方法只能讓同優(yōu)先級的線程有執(zhí)行的機會。

2.為什么每個線程要使用工作內(nèi)存因妙,而不直接訪問主內(nèi)存
①為了減少各線程對主內(nèi)存的頻繁訪問痰憎,各線程只需要訪問自己的工作內(nèi)存即可以獲取共享變量的副本值,將訪問壓力分?jǐn)偟礁骶€程的工作內(nèi)存中攀涵,主內(nèi)存只需要按照一定的規(guī)則同步共享變量的值到各線程的工作內(nèi)存铣耘。
注意:
“synchronized” — 保證在塊開始時都同步主內(nèi)存的值到工作內(nèi)存,而塊結(jié)束時將變量同步回主內(nèi)存

3.happens-before原則

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末以故,一起剝皮案震驚了整個濱河市蜗细,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌怒详,老刑警劉巖炉媒,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異昆烁,居然都是意外死亡吊骤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門善玫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來水援,“玉大人,你說我怎么就攤上這事茅郎∥显” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵系冗,是天一觀的道長奕扣。 經(jīng)常有香客問我共耍,道長叔汁,這世上最難降的妖魔是什么碴倾? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任跪解,我火速辦了婚禮,結(jié)果婚禮上楷兽,老公的妹妹穿的比我還像新娘地熄。我一直安慰自己,他們只是感情好芯杀,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布端考。 她就那樣靜靜地躺著,像睡著了一般揭厚。 火紅的嫁衣襯著肌膚如雪却特。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天筛圆,我揣著相機與錄音裂明,去河邊找鬼。 笑死太援,一個胖子當(dāng)著我的面吹牛闽晦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粉寞,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼尼荆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了唧垦?” 一聲冷哼從身側(cè)響起捅儒,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎振亮,沒想到半個月后巧还,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡坊秸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年麸祷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褒搔。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡阶牍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出星瘾,到底是詐尸還是另有隱情走孽,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布琳状,位于F島的核電站磕瓷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜困食,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一边翁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硕盹,春花似錦符匾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仰剿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痴晦,已是汗流浹背南吮。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留誊酌,地道東北人部凑。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像碧浊,于是被迫代替她去往敵國和親涂邀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

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

  • 從三月份找實習(xí)到現(xiàn)在箱锐,面了一些公司比勉,掛了不少,但最終還是拿到小米驹止、百度浩聋、阿里、京東臊恋、新浪衣洁、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,207評論 11 349
  • 二抖仅、共享受限資源 有了并發(fā)就可以同時做多件事情了坊夫。但是,兩個或多個線程彼此互相干涉的問題也就出現(xiàn)了撤卢。如果不防范這種...
    端木軒閱讀 495評論 0 0
  • 一环凿、認(rèn)識多任務(wù)、多進程凸丸、單線程拷邢、多線程 要認(rèn)識多線程就要從操作系統(tǒng)的原理說起。 以前古老的DOS操作系統(tǒng)(V 6....
    GT921閱讀 1,011評論 0 3
  • [cp]在這超現(xiàn)實的時代 青澀的愛情無處不在 沒有世俗沒有背叛 只有每晚通話的愛戀 時間一點一點過 他們也只有享受...
    聽說那個夢很遙遠(yuǎn)閱讀 119評論 0 0
  • 前言:會員卡的線上營銷方案不僅僅在推廣會員卡這一個服務(wù)屎慢,其中還必須有品牌推廣和拉新客戶瞭稼、活躍老客戶一并進行忽洛,才可以...
    宇藍(lán)醬閱讀 2,517評論 0 0