【JavaSE】多線(xiàn)程

一渴析、多線(xiàn)程


在有多任務(wù)執(zhí)行的需求時(shí)晚伙,如果沒(méi)有任務(wù)并行,不需要執(zhí)行多線(xiàn)程俭茧。

1.優(yōu)點(diǎn)

  • 資源利用率更好
  • 效率更高

2.缺點(diǎn)

  • 程序設(shè)計(jì)復(fù)雜咆疗。
  • 需要考慮可能會(huì)出現(xiàn)的數(shù)據(jù)不安全的問(wèn)題。

3.線(xiàn)程和進(jìn)程

3.1進(jìn)程

執(zhí)行中的程序叫做進(jìn)程(process)母债,是一個(gè)動(dòng)態(tài)的概念午磁。

  • 每個(gè)進(jìn)程由cpu、data毡们、code組成迅皇,且每個(gè)進(jìn)程都是獨(dú)立的,即便一個(gè)程序產(chǎn)生好幾個(gè)進(jìn)程
  • 在執(zhí)行多任務(wù)時(shí)衙熔,操作系統(tǒng)會(huì)將CPU動(dòng)態(tài)的分配給多個(gè)進(jìn)程登颓,每個(gè)進(jìn)程獨(dú)立運(yùn)行
  • 進(jìn)程是程序的一次動(dòng)態(tài)執(zhí)行過(guò)程,會(huì)獨(dú)占特定的地址空間

3.2線(xiàn)程

線(xiàn)程(Thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位青责。他被包含在進(jìn)程之中挺据,是進(jìn)程中的實(shí)際運(yùn)作單位取具。一條線(xiàn)程指的是進(jìn)程中單一順序的控制流,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線(xiàn)程扁耐,每條線(xiàn)程并行執(zhí)行多個(gè)線(xiàn)程暇检,每條線(xiàn)程并行執(zhí)行不同的任務(wù)。

3.3線(xiàn)程與進(jìn)程的區(qū)別

區(qū)別 進(jìn)程 線(xiàn)程
根本區(qū)別 作為資源分配的單位 調(diào)度和執(zhí)行的單位
開(kāi)銷(xiāo) 每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(進(jìn)程上下文)婉称,進(jìn)程間的切換會(huì)有較大的開(kāi)銷(xiāo) 線(xiàn)程可以看做輕量級(jí)的進(jìn)程块仆,同一類(lèi)線(xiàn)程共享代碼和數(shù)據(jù)空間,每個(gè)線(xiàn)程有獨(dú)立運(yùn)行棧和程序計(jì)數(shù)器(PC)王暗,線(xiàn)程切換的開(kāi)銷(xiāo)小
所處環(huán)境 在操作系統(tǒng)中能同時(shí)運(yùn)行多個(gè)任務(wù)(程序) 在同一應(yīng)用程序中有多個(gè)順序流同時(shí)執(zhí)行
分配內(nèi)存 系統(tǒng)在運(yùn)行的時(shí)候會(huì)為每個(gè)進(jìn)程分配不同的內(nèi)存區(qū)域 線(xiàn)程間共享進(jìn)程的所有資源悔据,每個(gè)線(xiàn)程只有有自己的堆棧和局部變量。線(xiàn)程由CPU獨(dú)立調(diào)度執(zhí)行俗壹,在多CPU環(huán)境下就允許多個(gè)線(xiàn)程同時(shí)運(yùn)行
包含關(guān)系 沒(méi)有線(xiàn)程的進(jìn)程可以看作單線(xiàn)程科汗,如果一個(gè)進(jìn)程擁有多個(gè)線(xiàn)程,則執(zhí)行過(guò)程不是一條線(xiàn)的绷雏,而是多條線(xiàn)(線(xiàn)程)共同完成的 線(xiàn)程是進(jìn)程的一部分头滔,所以線(xiàn)程有的時(shí)候會(huì)被稱(chēng)為是輕量級(jí)進(jìn)程或輕權(quán)進(jìn)程

4.并發(fā)和并行

并行一定是并發(fā),并發(fā)不一定是并行(也可能是串行)

并發(fā):一個(gè)時(shí)間段內(nèi)涎显,程序擁有處理多任務(wù)的能力

并行:一個(gè)時(shí)間段內(nèi)坤检,程序擁有同時(shí)處理多任務(wù)的能力

5.多線(xiàn)程的目標(biāo)

  • 創(chuàng)建與開(kāi)啟
  • 線(xiàn)程安全
  • 線(xiàn)程狀態(tài)
  • 線(xiàn)程通信

二、創(chuàng)建線(xiàn)程


1.繼承Thread

public class ThreadDemo extends Thread{

    @Override
    public void run() {
        for (int i = 0;i < 10;i++){
            System.out.println("-----"+i);
        }
    }

    public static void main(String[] args) {
        ThreadDemo td = new ThreadDemo();
        td.start();
        for (int i = 0;i < 10;i++){
            System.out.println("+++++"+i);
        }
    }
}

繼承Thread實(shí)現(xiàn)多線(xiàn)程

2.實(shí)現(xiàn)Runnable接口

通過(guò)實(shí)現(xiàn)Runnable接口重寫(xiě)run()方法期吓,方法內(nèi)定義線(xiàn)程體早歇,調(diào)用start()方法

public class ThreadDemo02 implements Runnable{

    @Override
    public void run() {
        for (int i = 0;i < 10;i++){
            System.out.println("-----"+i);
        }
    }

    public static void main(String[] args) {
        Thread t = new Thread(new ThreadDemo02());
        t.start();
        for (int i = 0;i < 10;i++){
            System.out.println("+++++"+i);
        }
    }
}

注意:run()方法中既不能拋出異常,也不能有返回值讨勤。

3.實(shí)現(xiàn)Callable接口(僅作了解)

重寫(xiě)call方法箭跳,方法中定義線(xiàn)程體,且有返回值

三悬襟、線(xiàn)程狀態(tài)


1.線(xiàn)程的五種狀態(tài)

  • 新生狀態(tài):通過(guò)new新建線(xiàn)程對(duì)象衅码,此時(shí)線(xiàn)程處于新生狀態(tài)
  • 就緒狀態(tài):調(diào)用start()方法后拯刁,沒(méi)有立即執(zhí)行脊岳,而是進(jìn)入就緒狀態(tài),等待CPU的調(diào)度
  • 運(yùn)行狀態(tài):當(dāng)CPU調(diào)度某一個(gè)線(xiàn)程時(shí)垛玻,這個(gè)線(xiàn)程獲取線(xiàn)程體割捅,執(zhí)行線(xiàn)程體代碼
  • 阻塞狀態(tài):線(xiàn)程無(wú)法正常運(yùn)行,如執(zhí)行了 sleep(睡眠)方法帚桩,或等待 I/O 設(shè)備等資源亿驾,將讓出 CPU 并暫時(shí)停止自己 運(yùn)行,進(jìn)入阻塞狀態(tài)
  • 終止?fàn)顟B(tài):死亡狀態(tài)是線(xiàn)程生命周期中的最后一個(gè)階段账嚎。線(xiàn)程死亡的原因有三個(gè)莫瞬,一個(gè)是正常運(yùn)行的線(xiàn)程完成了全部工作儡蔓;另一個(gè)是線(xiàn)程被強(qiáng)制性地終止,此方法不推薦使用疼邀;第三是線(xiàn)程拋出未捕獲的異常喂江。

Thread.State getState() 返回此線(xiàn)程的狀態(tài)

Thread.State 枚舉類(lèi) 標(biāo)識(shí)線(xiàn)程現(xiàn)有狀態(tài)

注意:

? 一個(gè)線(xiàn)程如果一旦進(jìn)入終止?fàn)顟B(tài)無(wú)法恢復(fù),new之后也是一個(gè)新的線(xiàn)程旁振。

? 如果一個(gè)線(xiàn)程一旦進(jìn)入到阻塞狀態(tài)获询,阻塞解除會(huì)直接恢復(fù)到就緒狀態(tài)不會(huì)直接恢復(fù)到運(yùn)行狀態(tài)。

2.如何進(jìn)入就緒狀態(tài)

  1. start()方法
  2. 阻塞解除
  3. yeild()禮讓線(xiàn)程
  4. 線(xiàn)程切換

3.如何進(jìn)入阻塞狀態(tài)

  1. sleep()
  2. join狀態(tài)
  3. wait()等待

4.如何進(jìn)入終止?fàn)顟B(tài)

  1. stop()方法拐袜,不推薦
  2. destory()方法吉嚣,不推薦
  3. 通過(guò)添加標(biāo)識(shí)判斷

四、暫停線(xiàn)程執(zhí)行


1.休眠線(xiàn)程(sleep)

sleep(long millis) 導(dǎo)致當(dāng)前正在執(zhí)行的線(xiàn)程休眠(暫時(shí)停止執(zhí)行)指定的毫秒數(shù)蹬铺,具體取決于系統(tǒng)計(jì)時(shí)器和調(diào)度程序的精度和準(zhǔn)確性尝哆。

特點(diǎn):不會(huì)釋放鎖,Sleep 時(shí)別的線(xiàn)程也不可以訪問(wèn)鎖定對(duì)象

作用:

  1. 模擬網(wǎng)絡(luò)延遲
  2. 顯現(xiàn)方法問(wèn)題的可能性

2.禮讓線(xiàn)程(yield)

static void yield() 當(dāng)前正在執(zhí)行的線(xiàn)程暫停一次甜攀,允許其他線(xiàn)程 執(zhí)行,不阻塞较解,線(xiàn)程進(jìn)入 就緒狀態(tài),如果沒(méi)有其他等待的線(xiàn)程,這個(gè) 時(shí)候當(dāng)前線(xiàn)程就會(huì)馬上恢復(fù)執(zhí)行赴邻。

特點(diǎn): yield()方法為靜態(tài)方法印衔,讓出 CPU 的使用權(quán),從運(yùn)行態(tài)直接進(jìn)入就緒態(tài)姥敛。讓 CPU重新挑選哪一個(gè)線(xiàn)程進(jìn)入運(yùn)行狀態(tài)奸焙。

3.插隊(duì)線(xiàn)程(join)

final void join() 調(diào)用該方法的線(xiàn)程強(qiáng)制執(zhí)行,其它線(xiàn)程處于阻 塞狀態(tài)彤敛,該線(xiàn)程執(zhí)行完畢后与帆,其它線(xiàn)程再執(zhí)行。

特點(diǎn):當(dāng)某個(gè)線(xiàn)程等待另一個(gè)線(xiàn)程執(zhí)行結(jié)束后墨榄,才繼續(xù)執(zhí)行時(shí)玄糟,使調(diào)用該方法的線(xiàn)程在此之前執(zhí)行完畢,也就是等待調(diào)用該方法的線(xiàn)程執(zhí)行完畢后再往下繼續(xù)執(zhí)行

五袄秩、線(xiàn)程優(yōu)先級(jí)


線(xiàn)程優(yōu)先級(jí)被分為整數(shù)1~10阵翎,且默認(rèn)的優(yōu)先級(jí)為5.

final int getPriority() 獲取線(xiàn)程的優(yōu)先級(jí)

final void setPriority(int priority) 設(shè)置線(xiàn)程的優(yōu)先級(jí)

Thread.MIN_PRIORITY = 1 
Thread.MAX_PRIORITY = 10 
Thread.NORM_PRIORITY = 5

注意:

優(yōu)先級(jí)低只是意味著獲得調(diào)度的概率低。并不是絕對(duì)先調(diào)用優(yōu)先級(jí)高后調(diào)用優(yōu)先級(jí)低的線(xiàn)程之剧。

public class ThreadTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(new ThreadDemo(), "t1");
        Thread t2 = new Thread(new ThreadDemo(), "t2");
        t1.setPriority(1);
        t2.setPriority(10);
        t1.start();
        t2.start();
    }
}

class ThreadDemo extends Thread {
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            // yield();
        }
    }
}

六郭卫、同步與死鎖


1.線(xiàn)程安全

一個(gè)情況下,我們?cè)跒榱颂岣邎?zhí)行效率背稼,我們可能會(huì)使用多線(xiàn)程同時(shí)執(zhí)行多個(gè)方法贰军,對(duì)同一數(shù)據(jù)進(jìn)行多次調(diào)用和覆蓋,此時(shí)蟹肘,數(shù)據(jù)會(huì)造成不準(zhǔn)確和不安全的問(wèn)題词疼。

public class TestSync {
    public static void main(String[] args) {
        Account a1 = new Account(100, "高");
        Drawing draw1 = new Drawing(80, a1);
        Drawing draw2 = new Drawing(80, a1);
        draw1.start(); //你取錢(qián)
        draw2.start(); //你老婆取錢(qián)
    }
}

/**
 * 簡(jiǎn)單表示銀行賬戶(hù)俯树,將來(lái)打算多個(gè)線(xiàn)程共用的資源
 */
class Account {
    int money;
    String aname;

    public Account(int money, String aname) {
        super();
        this.money = money;
        this.aname = aname;
    }
}

/*** 模擬提款操作 * @author Administrator **/
class Drawing extends Thread {
    int drawingNum; //取多少錢(qián)
    Account account; //要取錢(qián)的賬戶(hù)
    int expenseTotal; //總共取的錢(qián)數(shù)

    public Drawing(int drawingNum, Account account) {
        super();
        this.drawingNum = drawingNum;
        this.account = account;
    }

    @Override
    public void run() {
        if (account.money - drawingNum < 0) {
            return;
        }
        try {
            Thread.sleep(1000); //判斷完后阻 塞。其他線(xiàn)程開(kāi)始運(yùn)行贰盗。
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money -= drawingNum;
        expenseTotal += drawingNum;
        System.out.println(this.getName() + "-- 賬戶(hù)余額:" + account.money);
        System.out.println(this.getName() + "-- 總共取了:" + expenseTotal);
    }
}
多線(xiàn)程下的數(shù)據(jù)不安全

2.線(xiàn)程同步 synchonized

synchonized:同步鎖|排他鎖

線(xiàn)程同步:即當(dāng)有一個(gè)線(xiàn)程在對(duì)內(nèi)存進(jìn)行操作時(shí)聘萨,其他線(xiàn)程都不可以對(duì)這個(gè)內(nèi)存地址進(jìn)行操作,直到該線(xiàn)程完成操作童太, 其他線(xiàn)程才能對(duì)該內(nèi)存地址進(jìn)行操作米辐,而其他線(xiàn)程又處于等待狀態(tài)。

同步:協(xié)同书释、協(xié)助翘贮、互相配合。

2.1 synchornized 方法

public synchronized void accessVal(int newVal){

}

synchronized 方法控制對(duì)類(lèi)成員變量的訪問(wèn):每個(gè)對(duì)象對(duì)應(yīng)一把鎖爆惧,每個(gè) synchronized 方法都必須獲得調(diào)用該 方法的對(duì)象的鎖方能執(zhí)行狸页,否則所屬線(xiàn)程阻塞,方法一 旦執(zhí)行扯再,就獨(dú)占該鎖芍耘,直到從該方法返回時(shí)才將鎖釋放,此后被阻塞的線(xiàn)程方能獲得 該鎖熄阻,重新進(jìn)入可執(zhí)行狀態(tài)斋竞。

synchronized 方法的缺陷:若將一個(gè)大的方法聲明為synchronized 將會(huì)大大影響效率,典型地秃殉,若將線(xiàn)程類(lèi)的方法 run() 聲明為 synchronized 坝初,由于在線(xiàn)程的整個(gè)生命期內(nèi)它一直在運(yùn)行,因此將導(dǎo)致它對(duì)本類(lèi)任何synchronized 方法的調(diào)用都永遠(yuǎn)不會(huì)成功钾军。

2.2 synchoinized 塊

在代碼塊前加上 synchronized 關(guān)鍵字鳄袍,并指定加鎖的對(duì)象

synchronized(syncObject){ 
    //允許訪問(wèn)控制的代碼 
}
public class MyClass {
    public static synchronized void log1(String msg1, String msg2){
        log.writeln(msg1);
        log.writeln(msg2);
    }

    public static void log2(String msg1, String msg2){
        synchronized(MyClass.class){
            log.writeln(msg1);
            log.writeln(msg2);
        }
    }
}

注意:

  • 鎖中內(nèi)容,要鎖不變的東西吏恭,自定義的引用數(shù)據(jù)類(lèi)型的對(duì)象地址拗小,肯定不變
  • 代碼范圍太大,容易鎖不住內(nèi)容

雙重檢查(double check)提高效率樱哼,縮小范圍

3.鎖

3.1 鎖類(lèi)

相當(dāng)于鎖住了類(lèi)的所有對(duì)象哀九,有些對(duì)象不需要鎖時(shí),可以選擇使用this

3.2 鎖this

相當(dāng)于鎖住這個(gè)對(duì)象的所有資源唇礁,當(dāng)只想鎖中其中的一個(gè)資源的時(shí)候勾栗,可以只鎖資源

3.3 鎖資源

自定義的引用數(shù)據(jù)類(lèi)型

七惨篱、線(xiàn)程通信


使用Object類(lèi)中的wait()盏筐、notify()|notifyAll()實(shí)現(xiàn)線(xiàn)程通信

  1. wait()等待,當(dāng)一個(gè)線(xiàn)程wait()方法的時(shí)候進(jìn)入到某一個(gè)對(duì)象的等待池中進(jìn)行等待砸讳,等待喚醒并掛起
  2. notify()喚醒琢融,喚醒該對(duì)象等待池中正在等待的線(xiàn)程對(duì)象
  3. wait()notify()界牡、notifyAll() 方法必須使用同步環(huán)境下,用來(lái)作為多線(xiàn)程之間數(shù)據(jù)協(xié)調(diào)問(wèn)題,要使用在同步環(huán)境下控制線(xiàn)程安全漾抬,否則會(huì)拋出異常
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宿亡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纳令,更是在濱河造成了極大的恐慌挽荠,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件平绩,死亡現(xiàn)場(chǎng)離奇詭異圈匆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)捏雌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)跃赚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人性湿,你說(shuō)我怎么就攤上這事纬傲。” “怎么了肤频?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵叹括,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我宵荒,道長(zhǎng)领猾,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任骇扇,我火速辦了婚禮摔竿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘少孝。我一直安慰自己继低,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布稍走。 她就那樣靜靜地躺著袁翁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪婿脸。 梳的紋絲不亂的頭發(fā)上粱胜,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音狐树,去河邊找鬼焙压。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涯曲。 我是一名探鬼主播野哭,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼幻件!你這毒婦竟也來(lái)了拨黔?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绰沥,失蹤者是張志新(化名)和其女友劉穎篱蝇,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體徽曲,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡态兴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疟位。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞻润。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖甜刻,靈堂內(nèi)的尸體忽然破棺而出绍撞,到底是詐尸還是另有隱情,我是刑警寧澤得院,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布傻铣,位于F島的核電站,受9級(jí)特大地震影響祥绞,放射性物質(zhì)發(fā)生泄漏非洲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一蜕径、第九天 我趴在偏房一處隱蔽的房頂上張望两踏。 院中可真熱鬧,春花似錦兜喻、人聲如沸梦染。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)帕识。三九已至,卻和暖如春遂铡,著一層夾襖步出監(jiān)牢的瞬間肮疗,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工扒接, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留们衙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓砍艾,卻偏偏與公主長(zhǎng)得像巍举,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凝垛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360