JavaSE進(jìn)階十 線程二

死鎖

什么是死鎖

  • 死鎖是指兩個或兩個以上的線程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象吵聪,
    若無外力作用,它們都將無法推進(jìn)下去。
  • 此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖匪傍,這些永遠(yuǎn)在互相等待的線程稱為死鎖線程。

產(chǎn)生死鎖的四個必要條件:

  • 互斥條件:一個資源每次只能被一個線程使用
  • 請求與保持條件:一個線程因請求資源而阻塞時觉痛,對已獲得的資源保持不放
  • 不剝奪條件:線程已獲得的資源役衡,在未使用完之前,不能強(qiáng)行剝奪
  • 循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相連的循環(huán)等待資源關(guān)系
代碼示例

寫一個死鎖代碼薪棒,只有會寫手蝎,才會在以后的開發(fā)中注意這個事。

public class DeadLockTest01 {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        MyThread1 thread1 = new MyThread1(o1,o2);
        MyThread2 thread2 = new MyThread2(o1,o2);

        thread1.start();
        thread2.start();
    }
}

class MyThread1 extends Thread{
    Object obj1;
    Object obj2;
    public void run(){
        synchronized (obj1){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj2){

            }
        }
    }

    public MyThread1(Object obj1, Object obj2) {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }
}

class MyThread2 extends Thread{
    Object obj1;
    Object obj2;
    public void run(){
        synchronized (obj2){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj1){

            }
        }
    }

    public MyThread2(Object obj1, Object obj2) {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }
}

守護(hù)線程

  • java語言中線程分為兩大類

    • 用戶線程:
      • 主線程main方法就是一個用戶線程
    • 守護(hù)線程:
      • 最具有代表性的:垃圾回收線程(守護(hù)線程)俐芯。
  • 守護(hù)線程的特點:

    • 一般守護(hù)線程是一個死循環(huán)棵介,所有的用戶線程結(jié)束了,守護(hù)線程才會自動結(jié)束吧史。
  • 守護(hù)線程用在什么地方邮辽?

    每天00:00的時候系統(tǒng)數(shù)據(jù)自動備份。
    這個需要用到定時器,并且我們可以將定時器設(shè)置為守護(hù)線程吨述。一直在那守著岩睁,
    每到00:00的時候就備份一次;所有的用戶線程結(jié)束了揣云,守護(hù)線程自動退出捕儒,沒
    必要繼續(xù)數(shù)據(jù)備份了。
    
代碼示例
public class DaemonThreadTest01 {
    public static void main(String[] args) {
        BakDataThread bakDataThread = new BakDataThread();
        bakDataThread.setName("守護(hù)線程");
        // 啟動線程之前邓夕,將該線程設(shè)置為守護(hù)線程
        bakDataThread.setDaemon(true);
        bakDataThread.start();

        for (int i = 0;i < 10; i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

class BakDataThread extends Thread{
    public  void run(){
        int i= 0;
        //  即使是死循環(huán)刘莹,但由于該線程是守護(hù)者,當(dāng)用戶線程結(jié)束焚刚,守護(hù)線程自動終止点弯。
        while (true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->" + i++);
        }
    }
}

定時器

  • 定時器的作用:

    • 間隔特定的時間,執(zhí)行特定的程序矿咕。
    • 在實際開發(fā)中蒲拉,沒隔多久執(zhí)行一段特定的程序,這是常見的痴腌。
  • java中實現(xiàn)定時器有多種方式:

    • 1雌团,可以使用sleep方法,睡眠士聪,設(shè)置睡眠時間锦援;每到這個時間點醒來,執(zhí)行任務(wù)剥悟;
      這種方式是最原始的定時器灵寺。(比較low)
    • 2,使用java類庫中寫好的定時器区岗,java.util.Timer略板,可以直接拿來用。這種方式
      在目前的開發(fā)中也很少用慈缔,因為現(xiàn)在有很多高級框架都是支持定時任務(wù)的叮称。
    • 3,例如:現(xiàn)在常用的spring框架中提供的springTask框架藐鹤,這個框架只有進(jìn)行簡單
      的配置瓤檐,就可以完成定時器任務(wù)。
代碼示例
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerThreadTest01 {
    public static void main(String[] args) {
        // 創(chuàng)建定時器對象
        Timer timer = new Timer();
        // Timer timer = new Timer(true); //守護(hù)線程寫法

        //指定定時任務(wù)
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstTime = null;
        try {
            firstTime = sdf.parse("2021-06-03 17:53:15");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        // schedule(定時任務(wù),開始執(zhí)行時間,間隔多久執(zhí)行一次);
        timer.schedule(new LogTaskTimer(),firstTime,1000 * 10);
    }
}

// 編寫一個定時任務(wù)
class LogTaskTimer extends TimerTask{

    @Override
    public void run() {
        // 這里編寫需要執(zhí)行的任務(wù)
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sdf.format(new Date());
        System.out.println(time + "定時任務(wù)開始執(zhí)行了");
    }
}

實現(xiàn)線程的第三種方式:

  • 實現(xiàn)Callable接口娱节。(JDK8新特性)

    • 在java.util.concurrent包下挠蛉,屬于java并發(fā)包,老版JDK沒有肄满。
    • 優(yōu)點:這種方式實現(xiàn)的線程可以獲取線程執(zhí)行的返回值谴古。
    • 之前講解的那兩種方式是無法獲取線程返回值的质涛,因為run方法返回的是void。
    • 缺點:效率比較低掰担,在獲取某個線程執(zhí)行結(jié)果的時候汇陆,當(dāng)前線程受阻塞,效率較低恩敌。
代碼示例
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadTest09 {
    public static void main(String[] args) {
        // 第一步:創(chuàng)建一個"未來任務(wù)類"對象
        MyThreadCallable callable = new MyThreadCallable();
        FutureTask task = new FutureTask(callable);

        //創(chuàng)建線程對象
        Thread t = new Thread(task);
        t.setName("task");
        t.start();// 啟動線程

        // 在main方法中 獲取t線程的返回結(jié)果
        // task.get()方法的執(zhí)行會阻塞當(dāng)前線程
        try {
            Object obj = task.get();
            System.out.println(Thread.currentThread().getName() + "線程返回值是" + obj);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        // task.get()方法的執(zhí)行會阻塞main方法下面程序的執(zhí)行
        // main方法下面的程序想要執(zhí)行,必須等待get()方法執(zhí)行結(jié)束横媚,
        // get()方法可能需要很久纠炮,因為get()方法是為了拿另一個線程的執(zhí)行結(jié)果。
        System.out.println("hello world");

    }
}

class MyThreadCallable implements Callable{

    @Override
    public Object call() throws Exception {
        // call()方法就相當(dāng)于run方法灯蝴。只不過這個有返回值
        // 線程執(zhí)行一個任務(wù)恢口,執(zhí)行之后可能會有一個執(zhí)行結(jié)果
        // 模擬執(zhí)行
        System.out.println("call method begin");
        Thread.sleep(1000 * 5);
        System.out.println("call method end");
        int a = 100;
        int b = 200;
        return a + b ;
    }
}

關(guān)于Object類中的wait和notify方法(生產(chǎn)者和消費者模式!)

  • 什么是"生產(chǎn)者和消費者模式"穷躁?

    • 生產(chǎn)線程負(fù)責(zé)生產(chǎn)耕肩,消費線程負(fù)責(zé)消費。
    • 生產(chǎn)線程和消費線程要達(dá)到均衡问潭。
    • 這是一種特殊的業(yè)務(wù)需要猿诸,在這種特殊的情況下需要使用wait方法和notify方法。
  • wait方法和notify方法不是線程對象的方法狡忙,是java對象都有的方法梳虽,是Object類自帶的。

  • wait方法和notify方法是建立在synchronized線程同步的基礎(chǔ)之上灾茁,因為多線程要同時操作一個廠庫窜觉,有線程安全問題。

  • wait方法的作用:

     Object o = new Object();
    o.wait();
    
    • 表示讓正在o對象上活動的線程進(jìn)入等待狀態(tài)北专,并且釋放掉線程之前占用o對象的鎖禀挫;直到被喚醒為止。
  • notify方法的作用:

    • notify方法的調(diào)用可以讓正在o對象上等待的線程喚醒拓颓,只是通知语婴,不會釋放o對象上之前占有的鎖。
    • notifyAll方法:喚醒o對象上處于等待的所有線程驶睦。
代碼示例

模擬需求:
倉庫采用list集合腻格,list集合中假設(shè)只能存儲一個元素;一個元素表示倉庫滿了啥繁,
如果list集合中元素個數(shù)是0菜职,就表示倉庫空了;保證list集合永遠(yuǎn)都是最多存儲一個元素旗闽。
做到:生產(chǎn)一個消費一個酬核。

import java.util.ArrayList;
import java.util.List;

public class ThreadTest10 {
    public static void main(String[] args) {
        //創(chuàng)建一個共享對象
        List list = new ArrayList();

        // 創(chuàng)建兩個線程對象
        // 生產(chǎn)者線程
        Thread t1 = new Thread(new Producer(list));
        // 消費者線程
        Thread t2 = new Thread(new Consumer(list));

        t1.setName("生產(chǎn)者線程");
        t2.setName("消費者線程");

        // 啟動線程
        t1.start();
        t2.start();
    }
}

// 生產(chǎn)線程
class Producer implements Runnable{
    // 倉庫
    private List list;

    public Producer(List list) {
        this.list = list;
    }

    @Override
    public void run() { //一直生產(chǎn)
        while (true){
            // 給倉庫對象list加鎖
            synchronized (list){
                if (list.size() > 0){// 倉庫已滿
                    try {
                        list.wait(); // 當(dāng)前線程進(jìn)入等待狀態(tài)蜜另,并釋放Producer之前占有l(wèi)ist集合的鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 程序執(zhí)行到這里說明倉庫是空的,可以生產(chǎn)
                Object obj = new Object();
                list.add(obj);
                System.out.println(Thread.currentThread().getName() + "-->" + obj);
                // 喚醒消費者進(jìn)行消費
                list.notify();
                // list.notifyAll();
            }
        }
    }
}
// 消費線程
class Consumer implements Runnable{
    // 倉庫
    private List list;
    public Consumer(List list) {
         this.list = list;
    }
    @Override
    public void run() {//一直消費
        while (true){
            synchronized (list) {
                if (list.size() == 0) {// 倉庫空了
                    try {
                        list.wait(); // 消費者線程等待嫡意,釋放掉list集合的鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 程序執(zhí)行到此處說明倉庫中有數(shù)據(jù)举瑰,進(jìn)行消費。
                Object obj = list.remove(0);
                System.out.println(Thread.currentThread().getName() + "-->" + obj);

                //喚醒生產(chǎn)者生產(chǎn)
                list.notify();
                // list.notifyAll();
            }
        }
    }
}

上篇:JavaSE進(jìn)階十 線程一
下篇:JavaSE進(jìn)階十一 反射機(jī)制一

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蔬螟,一起剝皮案震驚了整個濱河市此迅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旧巾,老刑警劉巖耸序,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲁猩,居然都是意外死亡坎怪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門廓握,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搅窿,“玉大人,你說我怎么就攤上這事隙券∧杏Γ” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵娱仔,是天一觀的道長殉了。 經(jīng)常有香客問我,道長拟枚,這世上最難降的妖魔是什么薪铜? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮恩溅,結(jié)果婚禮上隔箍,老公的妹妹穿的比我還像新娘。我一直安慰自己脚乡,他們只是感情好蜒滩,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奶稠,像睡著了一般俯艰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锌订,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天竹握,我揣著相機(jī)與錄音,去河邊找鬼辆飘。 笑死啦辐,一個胖子當(dāng)著我的面吹牛谓传,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芹关,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼续挟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了侥衬?” 一聲冷哼從身側(cè)響起诗祸,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎轴总,沒想到半個月后直颅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡肘习,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年际乘,在試婚紗的時候發(fā)現(xiàn)自己被綠了坡倔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漂佩。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖罪塔,靈堂內(nèi)的尸體忽然破棺而出投蝉,到底是詐尸還是另有隱情,我是刑警寧澤征堪,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布瘩缆,位于F島的核電站,受9級特大地震影響佃蚜,放射性物質(zhì)發(fā)生泄漏庸娱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一谐算、第九天 我趴在偏房一處隱蔽的房頂上張望熟尉。 院中可真熱鬧,春花似錦洲脂、人聲如沸斤儿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽往果。三九已至,卻和暖如春一铅,著一層夾襖步出監(jiān)牢的瞬間陕贮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工潘飘, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留飘蚯,地道東北人馍迄。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像局骤,于是被迫代替她去往敵國和親攀圈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

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

  • 什么是進(jìn)程峦甩?什么是線程進(jìn)程是一個應(yīng)用程序(1個進(jìn)程是一個軟件)赘来。線程是一個進(jìn)程中的執(zhí)行場景/執(zhí)行單元。一個進(jìn)程可以...
    苦難_69e0閱讀 292評論 0 2
  • Java 多線程 學(xué)習(xí)筆記 一. 線程與進(jìn)程 進(jìn)程是一個應(yīng)用程序凯傲,線程是一個進(jìn)程中的執(zhí)行場景/執(zhí)行單元犬辰。一個進(jìn)程可...
    六比特派大錘閱讀 267評論 0 0
  • 相關(guān)概念 進(jìn)程是操作系統(tǒng)管理的,每個進(jìn)程都擁有自己獨立的內(nèi)存空間冰单,擁有自己獨立的一整套變量幌缝,進(jìn)程和進(jìn)程之間不共享內(nèi)...
    我的襪子都是洞閱讀 417評論 0 4
  • threading --- 基于線程的并行源代碼: Lib/threading.py 這個模塊在較低級的模塊 _t...
    青月教主閱讀 110評論 0 2
  • 表情是什么涵卵,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息荒叼。高興了當(dāng)然就笑了轿偎,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 124,187評論 2 7