多線程 - Day28,29 - 2019-04-25

總覽

  1. 多線程概述
  2. 多線程的實(shí)現(xiàn)方式
  3. 線程控制
  4. 線程的聲明周期
  5. 線程同步
  6. 死鎖問(wèn)題
  7. 線程間通信機(jī)制(等待喚醒機(jī)制)
  8. 線程池
  9. 定時(shí)器
  10. 與Collection間關(guān)系

1. 多線程概述

什么是多線程揣钦?為什么要使用多線程?

了解多線程必須先了解線程漠酿,要了解線程冯凹,必須先了解進(jìn)程。因?yàn)榫€程是依賴(lài)于進(jìn)程存在的炒嘲。

多進(jìn)程:一邊玩游戲宇姚,一邊聽(tīng)音樂(lè)匈庭。(多進(jìn)程的意義:充分利用計(jì)算機(jī)資源)
單核CPU:在同一時(shí)刻只能執(zhí)行一條指令。系統(tǒng)在不同進(jìn)程之間進(jìn)行高速的切換(時(shí)間片空凸,一般是毫秒級(jí))嚎花,給人類(lèi)一種錯(cuò)覺(jué),好像計(jì)算機(jī)在同一時(shí)刻只執(zhí)行一個(gè)程序

并發(fā):在同一時(shí)間段內(nèi)呀洲,執(zhí)行多個(gè)程序紊选,在同一時(shí)刻只執(zhí)行一個(gè)程序
并行:在同一時(shí)刻執(zhí)行多個(gè)程序

Java命令會(huì)啟動(dòng)JVM,即啟動(dòng)了一個(gè)進(jìn)程(JVM 就是一個(gè)進(jìn)程)
該進(jìn)程會(huì)啟動(dòng)一個(gè)主線程道逗,然后主線程調(diào)用某個(gè)類(lèi)的main方法兵罢,所以main方法都是運(yùn)行在主線程里。(之前我們寫(xiě)的基本都是單線程程序 )

JVM是單線程還是多線程滓窍?
JVM是多線程的卖词,至少會(huì)創(chuàng)建一個(gè)main線程和 一個(gè)GC線程。

幾個(gè)JVM吏夯? 每個(gè)Java進(jìn)程 分配一jvm個(gè)實(shí)例

Java 字節(jié)碼 解釋器此蜈。管理內(nèi)存。

  • 進(jìn)程:
    正在運(yùn)行的程序噪生,是系統(tǒng)進(jìn)行資源分配和調(diào)用的獨(dú)立單位裆赵。
    每一個(gè)進(jìn)程都有它自己的內(nèi)存空間和系統(tǒng)資源。
  • 線程:
    進(jìn)程的執(zhí)行路徑(任務(wù))
    一個(gè)進(jìn)程如果只有一條執(zhí)行路徑跺嗽,則稱(chēng)為 單線程程序战授。
    一個(gè)進(jìn)程如果有多條執(zhí)行路徑,則稱(chēng)為多線程程序(意義:提高程序的執(zhí)行率)桨嫁。

為什么要用多線程:

①. 為了更好的利用cpu的資源植兰,如果只有一個(gè)線程,則第二個(gè)任務(wù)必須等到第一個(gè)任務(wù)結(jié)束后才能進(jìn)行璃吧,如果使用多線程則在主線程執(zhí)行任務(wù)的同時(shí)可以執(zhí)行其他任務(wù)楣导,而不需要等待;

②. 進(jìn)程之間不能共享數(shù)據(jù)肚逸,線程可以爷辙;

③. 系統(tǒng)創(chuàng)建進(jìn)程需要為該進(jìn)程重新分配系統(tǒng)資源,創(chuàng)建線程代價(jià)比較须佟膝晾;

④. Java語(yǔ)言?xún)?nèi)置了多線程功能支持,簡(jiǎn)化了java多線程編程务冕。


2. 多線程的實(shí)現(xiàn)方式

一血当、繼承Thread
二、實(shí)現(xiàn)Runnable接口
三、線程池 (實(shí)現(xiàn)Callable接口臊旭,帶返回值

//1. 寫(xiě)一個(gè)類(lèi)繼承Thread
public class MyThread extends Thread{

    @Override
    public void run() {
        // super.run(); 沒(méi)有做任何事情
        // 如果只是一些簡(jiǎn)單的任務(wù)落恼,沒(méi)有必要新建一個(gè)線程。因?yàn)榫€程的創(chuàng)建和銷(xiāo)毀要消耗一定的系統(tǒng)資源离熏。
        // 線程里面執(zhí)行的往往是一些比較耗時(shí)的任務(wù)佳谦。
        // System.out.println("Hello, Thread");

        // 2. 重寫(xiě)run方法
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + ": " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Thread:
    線程是程序中的執(zhí)行線程。Java 虛擬機(jī)允許應(yīng)用程序并發(fā)地運(yùn)行多個(gè)執(zhí)行線程滋戳。

方法一:
    1. 寫(xiě)一個(gè)類(lèi)繼承Thread钻蔑。
    2. 重寫(xiě)run()方法。
    3. 創(chuàng)建子類(lèi)對(duì)象
    4. 啟動(dòng)線程

幾個(gè)小問(wèn)題:
1. 為什么要重寫(xiě)run()方法
    把線程要執(zhí)行的代碼封裝到run()里面奸鸯。

2. 啟動(dòng)線程使用的是那個(gè)方法?
    start()

3. 線程能不能多次啟動(dòng)?
    不能多次啟動(dòng)咪笑,如果啟動(dòng)多次會(huì)報(bào) IllegalThreadStateException。

4. run()和start()方法的區(qū)別
    run(): 封裝線程要執(zhí)行的代碼娄涩,直接調(diào)用相當(dāng)于普通的調(diào)用窗怒,不會(huì)創(chuàng)建新的線程。
    start(): 新建一個(gè)線程蓄拣,該線程執(zhí)行run().

 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        // 3. 創(chuàng)建子類(lèi)對(duì)象
        Thread thread = new MyThread();
        // 4. 啟動(dòng)線程
        // 直接執(zhí)行run()方法相當(dāng)于簡(jiǎn)單的方法調(diào)用
        // thread.run();
        // 啟動(dòng)線程:start(), start()會(huì)調(diào)用系統(tǒng)的API創(chuàng)建一個(gè)新的線程扬虚,該線程會(huì)執(zhí)行該對(duì)象的run()方法

        thread.start();
        thread.start();

        for (int i = 100; i < 2000; i++) {
            System.out.println(i);
        }
    }
}

如何獲取線程的名字:
    構(gòu)造方法:
        Thread(String name)

    String getName()
    void setName(String name)

問(wèn)題:如何獲取主線程的名字呢?
    static Thread currentThread()
          返回對(duì)當(dāng)前正在執(zhí)行的線程對(duì)象的引用球恤。

有了方式一孔轴,為什么還需要方式二呢?
    1. 解決Java的單繼承的局限性
    2. 便于多線程共享數(shù)據(jù)
    3. 將任務(wù)和線程分離碎捺,降低耦合性。


3. 線程的控制贷洲、調(diào)度

  • 線程睡眠
    static void sleep(long millis)
    在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)
    static void sleep(long millis, int nanos)
    在指定的毫秒數(shù)加指定的納秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)

  • 線程加入
    void join()
    等待該線程終止
    void join(long millis)
    等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒收厨。
    void join(long millis, int nanos)
    等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒 + nanos 納秒。

  • 線程禮讓
    static void yield()
    暫停當(dāng)前正在執(zhí)行的線程對(duì)象优构,并執(zhí)行其他線程
    當(dāng)前線程放棄執(zhí)行權(quán)诵叁,重新加入搶奪CPU的執(zhí)行權(quán)的行列。

  • 守護(hù)線程
    void setDaemon(boolean on)
    如果on為true, 將該線程標(biāo)記為守護(hù)線程钦椭。
    守護(hù)線程:被守護(hù)的線程如果結(jié)束了拧额,那么守護(hù)也會(huì)結(jié)束。

    問(wèn)題:
    誰(shuí)守護(hù)誰(shuí)彪腔?
    b.setDaemon(true), 將b為守護(hù)線程, 守護(hù)的對(duì)象:哪個(gè)線程運(yùn)行這行代碼侥锦,就守護(hù)那個(gè)線程。

  • 線程中斷
    void stop()
    中斷線程, 直接將線程干掉德挣,進(jìn)入死亡恭垦。(上課睡覺(jué),被AK掃射
    public void interrupt()
    中斷線程。會(huì)拋出InterriptedException, 走的異常處理的機(jī)制,后面的代碼還可以執(zhí)行番挺。(上課睡覺(jué)唠帝,被敲醒)

  • 線程的優(yōu)先級(jí)

    void setPriority(int newPriority)
    更改線程的優(yōu)先級(jí)

    int getPriority()
    返回線程的優(yōu)先級(jí)

    默認(rèn)為5
    public static final int MAX_PRIORITY 10
    public static final int MIN_PRIORITY 1
    public static final int NORM_PRIORITY 5

  • 線程調(diào)度

    1. 分時(shí)調(diào)度(雨露均沾)
    2. 搶占式調(diào)度(Java)

4. 線程的生命周期

  • 新建
  • 就緒
  • 運(yùn)行
  • 死亡
  • 阻塞
    • 一般阻塞
    • 等待阻塞
    • 同步阻塞

5. 線程的安全問(wèn)題

電影院售票問(wèn)題,引出線程安全問(wèn)題玄柏。
如何解決線程安全問(wèn)題襟衰?

  1. 同步代碼塊
    synchronized() 實(shí)質(zhì)上是鎖機(jī)制
  2. 同步方法
    synchronized
    • 普通成員方法:鎖的對(duì)象是調(diào)用此方法的對(duì)象,即this
    • 靜態(tài)方法:鎖的對(duì)象是字節(jié)碼文件對(duì)象粪摘,class類(lèi)對(duì)象
  3. 鎖Lock接口

6. 死鎖問(wèn)題

死鎖問(wèn)題:
在同步代碼塊嵌套的時(shí)候瀑晒,可能會(huì)出現(xiàn)多個(gè)線程相互等待的現(xiàn)象。

為了解決死鎖問(wèn)題赶熟,線程之間應(yīng)該相互配合瑰妄。


7. 線程間的通信機(jī)制

線程之間通信的機(jī)制(等待喚醒機(jī)制):
Object(鎖):
void notify() 喚醒一個(gè)正在等待獲取這個(gè)鎖對(duì)象的線程(隨機(jī))
void notifyAll() 喚醒所有正在等待獲取這個(gè)鎖對(duì)象的線程
void wait() 當(dāng)前線程進(jìn)入阻塞狀態(tài),并釋放鎖對(duì)象映砖,等待被喚醒间坐。
void wait(long timeout) 當(dāng)前線程進(jìn)入阻塞狀態(tài),并釋放鎖對(duì)象邑退,等待被喚醒竹宋。如果在這個(gè)時(shí)間內(nèi)沒(méi)有被喚醒,就自動(dòng)醒來(lái)地技。
void wait(long timeout, int nanos)

思考蜈七,為什么不定義在 Thread 類(lèi)中?
線程之間通信的媒介是鎖對(duì)象莫矗,但是鎖對(duì)象可以是任意對(duì)象飒硅。所以等待喚醒這些應(yīng)該定義Object類(lèi)中。

舉個(gè)栗子:生產(chǎn)者 - 消費(fèi)者模型
線程間還可以配合一起做某些事作谚,如交替數(shù)數(shù)


8. 線程池

為什么會(huì)出現(xiàn)線程池三娩?
啟動(dòng)一個(gè)新線程的成本是比較高的,因?yàn)樗婕暗脚c操作系統(tǒng)進(jìn)行交互妹懒。這種情況下使用線程池可以更好的提高性能雀监,尤其當(dāng)前程序需要?jiǎng)?chuàng)建大量的生存周期很短的線程時(shí),更應(yīng)該考慮線程池

  • 概述
    JDK5后出現(xiàn)的眨唬,養(yǎng)線程会前,當(dāng)線程執(zhí)行完任務(wù)后,不會(huì)銷(xiāo)毀線程匾竿,而是在回到線程池中成為空閑狀態(tài)瓦宜,等待分配任務(wù)

  • Executor:
    void execute(Runnable command)

  • Executors:
    public static ExecutorService newCachedThreadPool()
    // 創(chuàng)建一個(gè)默認(rèn)線程池,都是臨時(shí)工

    public static ExecutorService newFixedThreadPool(int nThreads)
    // 創(chuàng)建一個(gè)指定數(shù)量初始線程的線程池岭妖,都是正式員工

    public static ExecutorService newSingleThreadExecutor()
    // 創(chuàng)建單個(gè)線程的線程池歉提,只養(yǎng)一個(gè)

  • ExecutorService(線程池) extends Executor:

    • 提交任務(wù):
      void execute(Runnable command)

    Future<T> submit(Callable<T> task)
    Future<?> submit(Runnable task)
    <T> Future<T> submit(Runnable task, T result)

    • 關(guān)閉線程池:
      void shutdown()
      啟動(dòng)一次順序關(guān)閉笛坦,執(zhí)行以前提交的任務(wù),但不接受新任務(wù)苔巨。
      List<Runnable> shutdownNow()
      試圖停止所有正在執(zhí)行的活動(dòng)任務(wù)版扩,暫停處理正在等待的任務(wù),并返回等待執(zhí)行的任務(wù)列表侄泽。

Callable<T>: 返回結(jié)果并且可能拋出異常的任務(wù)
T call()

Future<T>:
V get()
如有必要礁芦,等待計(jì)算完成,然后獲取其結(jié)果悼尾。
V get(long timeout, TimeUnit unit)
如有必要柿扣,最多等待為使計(jì)算完成所給定的時(shí)間之后,獲取其結(jié)果(如果結(jié)果可用)闺魏。


9. 定時(shí)器

Timer(定時(shí)器)未状、TimerTask(定時(shí)器任務(wù))

  • 概述
    一種線程工具,以后臺(tái)線程的方式去執(zhí)行任務(wù)析桥∷静荩可安排任務(wù)執(zhí)行一次,或者定期重復(fù)執(zhí)行泡仗。以后使用定時(shí)任務(wù)埋虹,常常用任務(wù)調(diào)度框架: quartz。而不會(huì)使用Timer娩怎。但其底層原理是一樣的搔课。

  • 方法
    void schedule(TimerTask task, Date time)
    // 在指定的時(shí)間執(zhí)行指定任務(wù),執(zhí)行一次截亦。
    void schedule(TimerTask task, Date firstTime, long period)
    // 在指定的時(shí)間第一次執(zhí)行指定任務(wù)爬泥,每個(gè)多少毫秒,再重復(fù)的執(zhí)行這個(gè)任務(wù)
    void schedule(TimerTask task, long delay)
    // 在指定延遲時(shí)間后崩瓤,執(zhí)行執(zhí)行的任務(wù)急灭,執(zhí)行一次。
    void schedule(TimerTask task, long delay, long period)
    // 在指定延遲時(shí)間后第一次執(zhí)行指定任務(wù)谷遂,每個(gè)多少毫秒,再重復(fù)的執(zhí)行這個(gè)任務(wù)
    void cancel()
    // 終止此計(jì)時(shí)器卖鲤,丟棄所有當(dāng)前已安排的任務(wù)肾扰。

  • TimerTask:
    abstract void run() // 安排為一次執(zhí)行或重復(fù)執(zhí)行的任務(wù)。


10. Collection集合的線程安全

線程安全的集合:
Vector
HashTable
|-- Properties
StringBuffer
即使Vector是線程安全蛋逾,但是我們也不使用它集晚。

Collections:
static <T> List<T> synchronizedList(List<T> list) // 視圖技術(shù)
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
static <T> Set<T> synchronizedSet(Set<T> s)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市区匣,隨后出現(xiàn)的幾起案子偷拔,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莲绰,死亡現(xiàn)場(chǎng)離奇詭異欺旧,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蛤签,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)辞友,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人震肮,你說(shuō)我怎么就攤上這事称龙。” “怎么了戳晌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鲫尊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我沦偎,道長(zhǎng)疫向,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任扛施,我火速辦了婚禮鸿捧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好子姜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布颖御。 她就那樣靜靜地躺著,像睡著了一般震叮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天哗伯,我揣著相機(jī)與錄音,去河邊找鬼篷角。 笑死焊刹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的恳蹲。 我是一名探鬼主播虐块,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嘉蕾!你這毒婦竟也來(lái)了贺奠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤错忱,失蹤者是張志新(化名)和其女友劉穎儡率,沒(méi)想到半個(gè)月后挂据,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡儿普,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年崎逃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箕肃。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡婚脱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出勺像,到底是詐尸還是另有隱情障贸,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布吟宦,位于F島的核電站篮洁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏殃姓。R本人自食惡果不足惜袁波,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜗侈。 院中可真熱鬧篷牌,春花似錦、人聲如沸踏幻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)该面。三九已至夭苗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間隔缀,已是汗流浹背题造。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猾瘸,地道東北人界赔。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像牵触,于是被迫代替她去往敵國(guó)和親淮悼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

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

  • 該文章轉(zhuǎn)自:http://blog.csdn.net/evankaka/article/details/44153...
    加來(lái)依藍(lán)閱讀 7,350評(píng)論 3 87
  • Java-Review-Note——4.多線程 標(biāo)簽: JavaStudy PS:本來(lái)是分開(kāi)三篇的荒吏,后來(lái)想想還是整...
    coder_pig閱讀 1,648評(píng)論 2 17
  • ??一個(gè)任務(wù)通常就是一個(gè)程序,每個(gè)運(yùn)行中的程序就是一個(gè)進(jìn)程渊鞋。當(dāng)一個(gè)程序運(yùn)行時(shí)绰更,內(nèi)部可能包含了多個(gè)順序執(zhí)行流瞧挤,每個(gè)順...
    OmaiMoon閱讀 1,671評(píng)論 0 12
  • Java多線程學(xué)習(xí) [-] 一擴(kuò)展javalangThread類(lèi) 二實(shí)現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,957評(píng)論 1 18
  • 拔掉人際交往中的那根刺 文/張愛(ài)敏 案例6.我班有一女孩,內(nèi)向儡湾、拘謹(jǐn)特恬、少言。后來(lái)女孩終于向我敞開(kāi)心扉:她總是感覺(jué)別...
    愛(ài)于心敏于行閱讀 388評(píng)論 0 2