Java 多線程基礎(chǔ)

標(biāo)簽(空格分隔): java


線程和進(jìn)程

進(jìn)程具有獨(dú)立的數(shù)據(jù)空間碾盟,是系統(tǒng)進(jìn)行資源分配和調(diào)度的獨(dú)立單位

獨(dú)立性:進(jìn)程是系統(tǒng)中獨(dú)立存在的實(shí)體瓦糕,它可以擁有自己獨(dú)立的資源颓鲜,每一個進(jìn)程都擁有自己私有的地址空間辣卒,在沒有進(jìn) 過進(jìn)程本身允許的情況下拧晕,一個用戶進(jìn)程不可以訪問其他用戶的地址空間。
動態(tài)性:程序只是一個靜態(tài)的指令集合蜒茄,而進(jìn)程是正在操作系統(tǒng)中活著的指令集合唉擂。在進(jìn)程中加入了時間的概念,進(jìn)程具有自己的生命周期和個不同的狀態(tài)檀葛。這些概念在程序中都是不具備的玩祟。
并發(fā)性:多個進(jìn)程可以在多個處理器上并發(fā)執(zhí)行,多個進(jìn)程之間不會相互影響屿聋。

線程:是輕量級的進(jìn)程空扎,是進(jìn)程的執(zhí)行單元,它擁有自己的堆棧润讥,自己的程序計(jì)數(shù)器和自己的局部變量转锈,但不擁有系統(tǒng)資源,因?yàn)槎鄠€線程共享父進(jìn)程的全部資源楚殿。

一個線程可以創(chuàng)建和撤銷另一個線程撮慨,多個線程可以在一個進(jìn)程中并發(fā)執(zhí)行。

線程的創(chuàng)建和啟動

1脆粥、繼承Thread類創(chuàng)建線程

1.繼承Thread類砌溺,重寫run()方法。

public class MyThread extends Thread {

    @Override
    public void run() {
    }
}

2.實(shí)例化對象变隔,調(diào)用對象的start()方法

 MyThread thread = new MyThread();
 thread.start();

2规伐、使用靜態(tài)代理模式:實(shí)現(xiàn)Runnable接口

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

public class MyRun implements Runnable {
    @Override
    public void run() {
        //do Something
    }
}

2.new Thread對象,傳入Runnable接口實(shí)例,調(diào)用Thread類的start方法

 Thread thread = new Thread(new MyRun());
 thread.start();

兩種方式都可以創(chuàng)建線程匣缘,但是第二種方式猖闪,實(shí)現(xiàn)起來更加靈活,同時也可以實(shí)現(xiàn)多線程數(shù)據(jù)的共享肌厨,推薦使用第二種方式創(chuàng)建線程培慌。

3、實(shí)現(xiàn)Callable接口創(chuàng)建線程

實(shí)現(xiàn)Callable接口的類和實(shí)現(xiàn)Runnable接口的類都是可以被線程執(zhí)行的任務(wù)夏哭。
幾點(diǎn)不同:

  1. Callable規(guī)定的方法是call()方法检柬,而Runnable規(guī)定的方法是run()
  1. call()方法可以拋出異常献联,run()不能拋出異常
  2. Callable執(zhí)行任務(wù)后可以拿到返回值竖配,運(yùn)行Callable任務(wù)后可以拿到一個Future對象,Runnable的任務(wù)是不能帶有返回值的里逆,Future表示任務(wù)異步執(zhí)行的結(jié)果进胯,他提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成原押,并檢索計(jì)算的結(jié)果胁镐,通過Futrue對象,可以了解任務(wù)執(zhí)行的情況,可以取消任務(wù)的執(zhí)行盯漂,還可以獲取任務(wù)的執(zhí)行結(jié)果颇玷。

缺點(diǎn)是:比較繁瑣

實(shí)現(xiàn)步驟:

  1. 創(chuàng)建Callable實(shí)現(xiàn)類重寫call()方法
  2. 借助執(zhí)行調(diào)度服務(wù)ExecutorService獲取Future對象
    ExecutorService ser = Executors.newFixedThreadPool(2);
    Future result = ser.submit(實(shí)現(xiàn)類對象);
  3. 獲取值 result.get();
  4. 停止服務(wù)ser.shutdownNow();

第二種:實(shí)現(xiàn)方式
由于Callable接口不是Runnable接口的子類對象,因此不能直接作為Thread類的Target對象就缆,同時Callable接口里的call()方法擁有返回值帖渠,這個又該如何接受呢?
java為我們提供了一個Future接口竭宰,這個接口是為了用于接受Callable里的返回結(jié)果空郊,同時為我們提供了一個是實(shí)現(xiàn)類FutureTask,這個類同時實(shí)現(xiàn)了Future接口和Runnable接口切揭,這個類的實(shí)例需要一個Callable對象的實(shí)例狞甚,因此我們可以將Callable包裝成FutureTask的實(shí)例,這樣就同時解決了上面的兩個問題廓旬。

    private static void test() {
        MyCallable call = new MyCallable();
        FutureTask<Integer> task = new FutureTask<>(call);
        Thread thread = new Thread(task);
        try {
            System.out.println("主線程運(yùn)行");
            thread.start();
            int result = task.get();
            System.out.println("\n主線程阻塞完成");
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("程序退出");
    }
}

class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            System.out.print(i + "\t");
            sum += i;
        }
        return sum;
    }
}   

Thread類的常用方法

構(gòu)造方法

 /**
   這個是最全的構(gòu)造方法
   根據(jù)里面的參數(shù)可以組合出多種構(gòu)造
   stackSize是線程運(yùn)行時的堆棧大小
 */
Thread(ThreadGroup group, Runnable target, String name, long stackSize) 

靜態(tài)方法
    Thread.sleep();
    Thread.currentThread()

線程狀態(tài)(5種狀態(tài))

一哼审、新生狀態(tài):線程new出來時候的狀態(tài)。擁有自己的堆椩斜空間棺蛛。

二、就緒狀態(tài):新生狀態(tài)---->調(diào)用start()方法--->就緒狀態(tài)巩步。線程在執(zhí)行start()方法后并不是一定處于運(yùn)行的狀態(tài)旁赊,只是可以接受CPU調(diào)度,叫可運(yùn)行狀態(tài)----就緒狀態(tài)椅野,一旦獲取了CPU的執(zhí)行權(quán)终畅,線程就進(jìn)入運(yùn)行狀態(tài),并自動調(diào)用自己的run()竟闪。

三离福、運(yùn)行狀態(tài):就緒狀態(tài)的線程,在接受了CPU調(diào)度之后炼蛤,開始運(yùn)行妖爷,在運(yùn)行狀態(tài)的線程執(zhí)行自己run()方法中的代碼,除非調(diào)用其他方法而終止理朋,或等待某資源而阻塞絮识,有或運(yùn)行完成任務(wù)而死亡。如果在給定的時間片內(nèi)沒有執(zhí)行結(jié)束嗽上,就會被系統(tǒng)換下來回到等待執(zhí)行狀態(tài)(這個過程我們稱為cpu的任務(wù)切換---并發(fā)執(zhí)行)次舌。

四、終止?fàn)顟B(tài)(死亡狀態(tài)):當(dāng)線程體執(zhí)行完畢兽愤,則線程進(jìn)入終止?fàn)顟B(tài)彼念,線程結(jié)束挪圾。終止?fàn)顟B(tài)是線程生命周期中的最后一個階段。線程結(jié)束的原因有兩個逐沙,一個是運(yùn)行的線程完成了它的全部工作哲思,另一個是線程被強(qiáng)制性的終止,如執(zhí)行stop()方法和destroy()方法來終止線程(不推薦使用這兩個方法吩案,前者會產(chǎn)生異常也殖,后者是強(qiáng)制終止,不會釋放鎖)务热。

五忆嗜、阻塞狀態(tài):當(dāng)線程在運(yùn)行時,有阻塞的事件發(fā)生崎岂,則線程暫停運(yùn)行捆毫,進(jìn)入阻塞狀態(tài),進(jìn)入阻塞狀態(tài)的線程冲甘,無法沒有CPU執(zhí)行權(quán)绩卤,無法接受CPU的任務(wù)調(diào)度。當(dāng)阻塞解除時江醇,該線程將再次進(jìn)入就緒狀態(tài)濒憋,重新?lián)碛蠧PU執(zhí)行權(quán),可以接受CPU調(diào)度陶夜。處于正在運(yùn)行狀態(tài)的線程凛驮,在某些情況下,執(zhí)行了sleep()方法条辟,或等待IO設(shè)備等資源黔夭,或讓出CPU的執(zhí)行權(quán)并暫時停止運(yùn)行,進(jìn)入阻塞狀態(tài)羽嫡,在阻塞狀態(tài)的線程就不能就如就緒隊(duì)列本姥,只有當(dāng)引起阻塞的原因解除時,如睡眠時間到杭棵,或等待的IO設(shè)備空閑下來婚惫,線程便進(jìn)入就緒狀態(tài),重新到隊(duì)列中排隊(duì)等待魂爪,被系統(tǒng)選中后從原來停止的位置開始繼續(xù)運(yùn)行先舷。

停止線程的方法

停止一個線程很簡單,就是讓這個線程體執(zhí)行完成就行了甫窟,因此我們使用一個flag來判斷是否結(jié)束以及何時結(jié)束這個循環(huán)密浑,就行了。
一般的情況下:我們自己寫一個方法粗井,調(diào)用這個方法,則設(shè)置標(biāo)記位,結(jié)束運(yùn)行條件浇衬,促使線程體執(zhí)行結(jié)束懒构。

 class MyCallable implements Callable<Integer> {

public boolean flag = true;

@Override
public Integer call() throws Exception {
    int i = 0;
    for (; i < 20; i++) {
         if (!flag)
            break;
            Thread.sleep(1000);
            System.out.print(i + "\t");
    }
    return i;
}

/**
*為外界提供停止線程的控制方法
*/
  public void stop(){
     flag = false;
   }
 }

多線程并發(fā)安全問題

由于線程在系統(tǒng)的調(diào)度中具有隨機(jī)性,因此當(dāng)多個線程在訪問同一份資源時,很容易出現(xiàn)錯誤.

一些很經(jīng)典的問題:賣票和取錢,
下面我們根據(jù)一個經(jīng)典的賣票程序來分析一下和解決一下線程安全性問題

    private int ticket = 20;
    @Override
    public void run() {
        while (ticket>0) {
            ticket--;
            System.out.println(Thread.currentThread().getName() + "=====" +ticket );
           }
        }

當(dāng)上面的程序放到多線程中使用時,就會出現(xiàn)并發(fā)安全問題耘擂,可能出現(xiàn)的現(xiàn)象是:同一份資源出現(xiàn)重復(fù)調(diào)用胆剧,
例如,當(dāng)一個線程當(dāng)ticket--醉冤;代碼完成后現(xiàn)成的cpu資源被搶奪秩霍,這個時候,第二個線程也執(zhí)行了ticket--蚁阳;這個時候
出現(xiàn)數(shù)字丟失铃绒,有的字重復(fù)出現(xiàn),同時t--螺捐;在程序中也是分兩步執(zhí)行的颠悬。有可能執(zhí)行了減法操作,還沒來得及賦值定血,就被其它線程搶奪走了CPU執(zhí)行權(quán)赔癌,使得第一個減法操作無效丟失。

解決辦法:使用Synchronized同步--->同步代碼塊和同步函數(shù)

public class SaleTicket implements Runnable {

    private int ticket = 10;
    
    @Override
    public void run() {
        synchronized (SaleTicket.class) {
            while (ticket > 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket--;
                System.out.println(Thread.currentThread().getName() + "=====" + ticket);
            }
        }
    }

注:
如果代碼加了同步還是出現(xiàn)了安全問題澜沟,那么就看:

1.多個線程是不是操作的是同一份資源
2.同步的代碼地方是不是使用的是同一個鎖
3.是不是所有的共享數(shù)據(jù)資源都加入了同步

同步方法持有的鎖灾票,是該方法所屬的對象,靜態(tài)同步代碼塊持有的鎖茫虽,是該類的字節(jié)碼文件铝条。

單例設(shè)計(jì)模式---懶漢式

class Single{
  private static Single instance = null;
  private Single(){}

  public static Single getIntance(){
    if(instance == null){
        synchronized(Single.class){
          if(instance == null) {
              instance = new Single();
          }
       }
       return instance;
    }
}

懶漢式的單例模式的優(yōu)點(diǎn)是延時加載,但是在多線程中會出現(xiàn)問題席噩,使用同步代碼塊可以解決該問題班缰,使用雙重判斷可以稍微提高一下效率。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悼枢,一起剝皮案震驚了整個濱河市埠忘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌馒索,老刑警劉巖莹妒,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)擂橘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門碟刺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人司致,你說我怎么就攤上這事鸥昏〈婧停” “怎么了爽哎?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵蜓席,是天一觀的道長。 經(jīng)常有香客問我厨内,道長,這世上最難降的妖魔是什么渺贤? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任雏胃,我火速辦了婚禮,結(jié)果婚禮上志鞍,老公的妹妹穿的比我還像新娘瞭亮。我一直安慰自己,他們只是感情好述雾,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布街州。 她就那樣靜靜地躺著,像睡著了一般玻孟。 火紅的嫁衣襯著肌膚如雪唆缴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天黍翎,我揣著相機(jī)與錄音面徽,去河邊找鬼。 笑死匣掸,一個胖子當(dāng)著我的面吹牛趟紊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碰酝,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼霎匈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了送爸?” 一聲冷哼從身側(cè)響起铛嘱,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袭厂,沒想到半個月后墨吓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纹磺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年帖烘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片橄杨。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡秘症,死狀恐怖照卦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情历极,我是刑警寧澤窄瘟,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布衷佃,位于F島的核電站趟卸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏氏义。R本人自食惡果不足惜锄列,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惯悠。 院中可真熱鬧邻邮,春花似錦、人聲如沸克婶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽情萤。三九已至鸭蛙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筋岛,已是汗流浹背娶视。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睁宰,地道東北人肪获。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像柒傻,于是被迫代替她去往敵國和親孝赫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359