Java 線程間通信

線程間的通信


線程間通信:多個(gè)線程在處理同一資源,但是任務(wù)卻不同

例程.png

1 多線程執(zhí)行同一資源:

// 資源
class Resource {
    String name;
    String sex;
}

// 輸入
class Input implements Runnable {
    Resource r;
    Input(Resource r) {
        this.r = r;
    }
    public void run() {
        boolean b = true;
        while (true) {
            synchronized (Resource.class) {
                if (b) {
                    r.name = "zimo";
                    r.sex = "man";
                } else {
                    r.name = "子陌";
                    r.sex = "女";
                }
            }
            b = !(b);
        }
    }
}

// 輸出
class Output implements Runnable {
    Resource r;
    Output(Resource r) {
        this.r = r;
    }
    public void run() {
        while (true) {
            synchronized (Resource.class) {
                System.out.println(r.name + ":" + r.sex);
            }
        }
    }
}

class ResourceDemo {
    public static void main(String[] args) {
        // 創(chuàng)建資源
        Resource r = new Resource();
        // 創(chuàng)建任務(wù)
        Input in = new Input(r);
        Output out = new Output(r);
        // 創(chuàng)建線程
        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);
        // 開(kāi)啟線程
        t1.start();
        t2.start();
    }
}

2 多線程執(zhí)行同一資源 - 加入等待喚醒功能:

  1. wait()棵癣、notify()、notifyAll(),用來(lái)操作線程為什么定義在了Object類(lèi)中最盅?
    • 這些方法存在于同步中
    • 使用這些方法時(shí)必須要表示所屬的同步的鎖(對(duì)象監(jiān)視器)
    • 鎖可以是任意對(duì)象拨齐,所以任意對(duì)象調(diào)用的方法一定定義Object類(lèi)中
  2. wait()触机、sleep()有什么區(qū)別跪者?
    • wait():釋放cpu執(zhí)行權(quán),釋放鎖
    • sleep():釋放cpu執(zhí)行權(quán)妹卿,不釋放鎖
/*
* 涉及的方法:
* 1.wait();     讓線程處于凍結(jié)狀態(tài)葬荷,被wait的線程會(huì)被存儲(chǔ)到線程池中。
* 2.notify();     喚醒線程池中的一個(gè)線程(任意的纽帖,無(wú)序的)宠漩。
* 2.notifyAll();    喚醒線程池中的所有線程。
*/
// 資源
class Resource {
    String name;
    String sex;
    boolean flag = false;
}

// 輸入
class Input implements Runnable {
    Resource r;
    Input(Resource r) {
        this.r = r;
    }
    public void run() {
        boolean b = true;
        while (true) {
            synchronized (Resource.class) {
                if(r.flag){
                    // 如果有內(nèi)容懊直,那么寫(xiě)線程等待
                    r.wait();
                }else{
                    // 如果沒(méi)有內(nèi)容扒吁,就往里寫(xiě)
                    if (b) {
                        r.name = "zimo";
                        r.sex = "man";
                    } else {
                        r.name = "子陌";
                        r.sex = "女";
                    }
                    // 標(biāo)記修改
                    r.flag = true;
                    // 喚醒輸出線程
                    r.notify();
                    b = !(b);
                }
            }
        }
    }
}

// 輸出
class Output implements Runnable {
    Resource r;
    Output(Resource r) {
        this.r = r;
    }
    public void run() {
        while (true) {
            synchronized (Resource.class) {
                if(!r.flag){
                    r.wait();
                }else{
                    System.out.println(r.name + ":" + r.sex);
                    r.flag = false;
                    r.notify();
                }
            }
        }
    }
}

class ResourceDemo {
    public static void main(String[] args) {
        // 創(chuàng)建資源
        Resource r = new Resource();
        // 創(chuàng)建任務(wù)
        Input in = new Input(r);
        Output out = new Output(r);
        // 創(chuàng)建線程
        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);
        // 開(kāi)啟線程
        t1.start();
        t2.start();
    }
}

代碼優(yōu)化:

// 資源
class Resource {
    private String name;
    private String sex;
    private boolean flag = false;
    public synchronized void set(String name, String sex){
        if(flag){
            try{
                this.wait();
            }catch(InterruptedException e){
                
            }
        }
        this.name = name;
        this.sex = sex;
        flag = true;
        this.notify();
    }
    public synchronized void out(){
        if(!flag){
            try{
                this.wait();
            }catch(InterruptedException e){
                
            }
        }
        System.out.println("name :" + name + ",sex :" + sex);
        flag = false;
        this.notify();
    }
}

// 輸入
class Input implements Runnable {
    Resource r;
    Input(Resource r) {
        this.r = r;
    }
    public void run() {
        boolean b = true;
        while (true) {
            if (b) {
                r.set("zimo", "man");
            } else {
                r.set("子陌", "女");
            }
            b = !(b);
        }
    }
}

// 輸出
class Output implements Runnable {
    Resource r;
    Output(Resource r) {
        this.r = r;
    }
    public void run() {
        while (true) {
            r.out();
        }
    }
}

class ResourceDemo {
    public static void main(String[] args) {
        // 創(chuàng)建資源
        Resource r = new Resource();
        // 創(chuàng)建任務(wù)線程
        Thread t1 = new Thread(new Input(r));
        Thread t2 = new Thread(new Output(r));
        // 開(kāi)啟線程
        t1.start();
        t2.start();
    }
}

3 多生產(chǎn)者-多消費(fèi)者模式:

// 多生產(chǎn)者-多消費(fèi)者
// 產(chǎn)品資源
class Resource{
    private String name;
    private int count;
    private boolean flag = false;
    public synchronized void set(String name){
        while(flag){
            try{this.wait();}catch(InterruptedException e){}
        }
        this.name = name + this.count;
        count++;
        System.out.println(Thread.currentThread().getName() + "..生產(chǎn)者.." + this.name);
        
        flag = true;
        notifyAll();
    }
    public synchronized void out(){
        while(!flag){
            try{this.wait();}catch(InterruptedException e){}
        }
        System.out.println(Thread.currentThread().getName() + "..消費(fèi)者.." + this.name);
        flag = false;
        notifyAll();
    }
}

// 生產(chǎn)者
class Producer implements Runnable{
    private Resource r;
    Producer(Resource r){
        this.r = r;
    }
    public void run(){
        while(true){
            r.set("烤鴨");
        }
    }
}

// 消費(fèi)者
class Consumer implements Runnable{
    private Resource r;
    Consumer(Resource r){
        this.r = r;
    }
    public void run(){
        while(true){
            r.out();
        }
    }
}
class ProducerConsumerDemo{
    public static void main(String[] args){
        Resource r = new Resource();
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(pro);
        Thread t3 = new Thread(con);
        Thread t4 = new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
  • if標(biāo)記:只有一次判斷,會(huì)導(dǎo)致不該運(yùn)行的線程運(yùn)行了室囊,出現(xiàn)數(shù)據(jù)錯(cuò)誤的情況
  • while判斷標(biāo)記:解決了線程重新喚醒執(zhí)行權(quán)后雕崩,是否能繼續(xù)往下運(yùn)行!如果本方喚醒了本方融撞,沒(méi)有意義盼铁,notify + while 會(huì)導(dǎo)致死鎖
  • notifyAll:解決了,本方線程一定會(huì)喚醒對(duì)方線程的問(wèn)題

JDK升級(jí)1.5 - 同時(shí)更名為5.0版本尝偎,同理1.6為6.0

notify()不僅喚醒了對(duì)方饶火,同時(shí)喚醒了所有人鹏控,所以5.0推出java.util.concurrent.licks

同步代碼塊synchronized,對(duì)于鎖的操作是隱士的肤寝,jdk1.5后將同步鎖封裝成了對(duì)象当辐,并將鎖操作的隱式方式定義到了該對(duì)象中,將隱式動(dòng)作變成了顯示動(dòng)作

  • Lock替代了Condition方法的使用
  • Condition替代了Object監(jiān)視器方法的使用
class LockDemo{
    // 創(chuàng)建一個(gè)鎖對(duì)象
    Lock lock = new ReentrantLock();
    // 通過(guò)已有的鎖 獲取該鎖上的監(jiān)視器對(duì)象
    Condition c = lock.newCondition();
    //public synchronized void out(){
    public void out(){
        lock.lock();    /// 獲取鎖
        try{
            while(!flag){
                //try{this.wait();}catch(InterruptedException e){}
                try{c.await();}catch(InterruptedException e){}
            }
            System.out.println(Thread.currentThread().getName() + "..消費(fèi)者.." + this.name);
            flag = false;
            c.signalAll();
        }finally{
            lock.unlock();
        }
    }
    
    // 通過(guò)已有的鎖獲取兩組監(jiān)視器:一組監(jiān)視生產(chǎn)者鲤看,一組監(jiān)視消費(fèi)者
    // 生產(chǎn)的時(shí)候消費(fèi)不能動(dòng)
    
    // 創(chuàng)建一個(gè)鎖對(duì)象
    Lock lock = new ReentrantLock();
    // 通過(guò)已有的鎖 獲取該鎖上的監(jiān)視器對(duì)象
    Condition producer_con = lock.newCondition(); // 生產(chǎn)者監(jiān)視器
    Condition consumer_con = lock.newCondition(); // 消費(fèi)者監(jiān)視器

    public synchronized void set(String name){
        while(flag){
            try{producer_con.wait();}catch(InterruptedException e){}
        }
        this.name = name + this.count;
        count++;
        System.out.println(Thread.currentThread().getName() + "..生產(chǎn)者.." + this.name);
        
        flag = true;
        consumer_con.signal();
    }
    public void out(){
        lock.lock();    /// 獲取鎖
        try{
            while(!flag){
                //try{this.wait();}catch(InterruptedException e){}
                try{consumer_con.await();}catch(InterruptedException e){}
            }
            System.out.println(Thread.currentThread().getName() + "..消費(fèi)者.." + this.name);
            flag = false;
            producer_con.signal();
        }finally{
            lock.unlock();
        }
    }
}

線程喚醒等待解析.png
  • Lock接口:替代了同步代碼塊或者同步函數(shù)缘揪。將同步的隱式鎖操作變成顯示鎖操作。同時(shí)更為靈活义桂,可以一個(gè)鎖上加上多組監(jiān)視器
    • lock():獲取鎖
    • unlock():釋放鎖找筝,通常需要定義fianlly代碼塊中
  • Condition接口:替代了Object中的wait、notify慷吊、notifyAll方法袖裕,將這些監(jiān)視器方法單獨(dú)進(jìn)行了封裝,變成了Condition監(jiān)視器對(duì)象罢浇,可任意鎖進(jìn)行組合
    • await():等價(jià)于wait()
    • signal():等價(jià)于notify()
    • signalAll():等價(jià)于notifyAll()

4陆赋、wait()和sleep()的區(qū)別

  1. wait可以指定時(shí)間沐祷,也可以不指定時(shí)間

    sleep必須指定時(shí)間

  2. 在同步中時(shí)嚷闭,對(duì)于cpu的執(zhí)行權(quán)和鎖的處理不同

    • wait:釋放執(zhí)行權(quán),釋放鎖
    • sleep:釋放執(zhí)行權(quán)赖临,不釋放鎖

停止線程

  1. stop()方法
  2. run()方法結(jié)束

1) 定義循環(huán)結(jié)束標(biāo)記

  • 因?yàn)榫€程運(yùn)行代碼一般都是循環(huán)胞锰,只要控制了循環(huán)即可
class StopThread implements Runnable{
    private boolean flag = true;
    public void run(){
        while(flag){
            System.out.println(Thread.currentThread().getName() + ".....");
        }
    }
    public void setFlag(){
        this.flag = false;
    }
}

class StopThreadDemo{
    public static void main(String[] args){
        StopThread st = new StopThread();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
        
        int num = 1;
        for(;;){
            if(++num == 50){
                st.setFlag();
                break;
            }
            System.out.println("main...." + num);
        }
    }
}

如果線程處于凍結(jié)狀態(tài),無(wú)法讀取標(biāo)記兢榨,如何結(jié)束嗅榕?

2)使用interrupt(中斷)方法

  • 該方法是結(jié)束線程的凍結(jié)狀態(tài),使線程(強(qiáng)制)回到運(yùn)行狀態(tài)中來(lái)
  • 強(qiáng)制動(dòng)作會(huì)發(fā)生InterruptException異常吵聪,記得要處理
class StopThread implements Runnable{
    private boolean flag = true;
    public synchronized void run(){
        while(flag){
            try{
                wait();   // 當(dāng)主線程死亡凌那,子線程沒(méi)有結(jié)束進(jìn)入等待
            }catch(InterruptedException e){
                System.out.println(Thread.currentThread().getName() + "......" + e);
                flag = false;
            }
            System.out.println(Thread.currentThread().getName() + "*******");
        }
    }
    public void setFlag(){
        this.flag = false;
    }
}

class StopThreadDemo{
    public static void main(String[] args){
        StopThread st = new StopThread();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
        
        int num = 1;
        for(;;){
            if(++num == 50){
                //st.setFlag();   // 線程進(jìn)入凍結(jié),無(wú)法讀取標(biāo)記
                t1.interrupt();
                t2.interrupt();                
                break;
            }
            System.out.println("main...." + num);
        }
        System.out.println("main....end");
    }
}

\color{blue}{**stop方法已經(jīng)過(guò)時(shí)不再使用**}

3)守護(hù)線程(聯(lián)合線程/后臺(tái)線程)

  • 如果前臺(tái)線程都結(jié)束了吟逝,守護(hù)線程(后臺(tái)線程)無(wú)論處于什么狀態(tài)都將自動(dòng)結(jié)束
class StopThread implements Runnable{
    private boolean flag = true;
    public synchronized void run(){
        while(flag){
            try{
                wait();   // 當(dāng)主線程死亡帽蝶,子線程沒(méi)有結(jié)束進(jìn)入等待
            }catch(InterruptedException e){
                System.out.println(Thread.currentThread().getName() + "......" + e);
                flag = false;
            }
            System.out.println(Thread.currentThread().getName() + "*******");
        }
    }
    public void setFlag(){
        this.flag = false;
    }
}

class StopThreadDemo{
    public static void main(String[] args){
        StopThread st = new StopThread();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.setDaemon(true);   // 開(kāi)啟t2守護(hù)線程(后臺(tái)線程)
        t2.start();
        
        int num = 1;
        for(;;){
            if(++num == 50){
                t1.interrupt();
                //t2.interrupt();                
                break;
            }
            System.out.println("main...." + num);
        }
        System.out.println("main....end");
    }
}

線程的其他方法

  • join()方法:函數(shù)執(zhí)行線程釋放執(zhí)行權(quán)和執(zhí)行資格,等待調(diào)用join()方法的線程執(zhí)行完成
  • setPriority()方法:設(shè)置線程優(yōu)先級(jí)方法
  • yield():暫停一下块攒,釋放一下CPU的執(zhí)行權(quán)励稳,同時(shí)自己還有機(jī)會(huì)搶到
class Demo implements Runnable{
    public void run(){
        for(int i = 0; i < 50; i++){
            System.out.println(Thread.currentThread().toString() + "......" + i);
        }
    }
}
class JoinDemo{
    public static void main(String[] args){
        Demo d = new Demo();
        
        Thread t1 = new Thread(d);
        Thread t2 = new Thread(d);
        t1.start();
        t1.join();    // t1線程要申請(qǐng)加入進(jìn)來(lái),運(yùn)行囱井。臨時(shí)加入一個(gè)線程運(yùn)算時(shí)可以使用join()方法
        t2.start();
        t2.setPriority(Thread.MAX_PRIORITY);
        for(int i = 0; i < 50; i++){
            System.out.println(Thread.currentThread().getName() + "......" + i);
        }
    }
}

class ThreadTest{
    public static void main(String[] args){
        // main
        for(int i = 0; i < 50; i++){
            System.out.println(Thread.currentThread().getName() + "......" + i);
        }
        // Thread
        new Thread(){
            public void run(){
                for(int j = 0; j < 50; j++){
                    System.out.println(Thread.currentThread().getName() + "......" + j);
                }
            }
        }.start();
    // Runnable
        Runnable r = new Runnable(){
            public void run(){
                for(int k = 0; k < 50; k++){
                    System.out.println(Thread.currentThread().getName() + "......" + k);
                }
            }
        }
        new Thread(r).start();
    }
}

Thread創(chuàng)建線程的時(shí)候可以進(jìn)行組的劃分驹尼,可以同時(shí)進(jìn)行一整個(gè)線程組的操作


面試案例

class Test implements Runnable {
    public void run(Thread t){
        
    }
}
// 如果錯(cuò)誤,錯(cuò)誤發(fā)生在哪一行庞呕?
/*
* 第一行:抽象類(lèi)runnable接口沒(méi)有被覆蓋新翎,要么被abstract修飾
* run是子類(lèi)的Test特有的方法
*/


class ThreadTest {
    public static void main(String[] args){
        // 如果都沒(méi)有,以Thread自己為主
        new Thread(new Runnable(){
            // 沒(méi)有子類(lèi)方法,以任務(wù)為主
            public void run(){
                System.out.println("Runnable run");
            }
        }){
            // 子類(lèi)自己有方法料祠,以子類(lèi)為主
            public void run(){
                System.out.println("Thread run");
            }
        }.start();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末骆捧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子髓绽,更是在濱河造成了極大的恐慌敛苇,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顺呕,死亡現(xiàn)場(chǎng)離奇詭異枫攀,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)株茶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)来涨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人启盛,你說(shuō)我怎么就攤上這事蹦掐。” “怎么了僵闯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵卧抗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我鳖粟,道長(zhǎng)社裆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任向图,我火速辦了婚禮泳秀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘榄攀。我一直安慰自己嗜傅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布檩赢。 她就那樣靜靜地躺著吕嘀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漠畜。 梳的紋絲不亂的頭發(fā)上币他,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音憔狞,去河邊找鬼蝴悉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瘾敢,可吹牛的內(nèi)容都是我干的拍冠。 我是一名探鬼主播尿这,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庆杜!你這毒婦竟也來(lái)了射众?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晃财,失蹤者是張志新(化名)和其女友劉穎叨橱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體断盛,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡罗洗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了钢猛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伙菜。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖命迈,靈堂內(nèi)的尸體忽然破棺而出贩绕,到底是詐尸還是另有隱情,我是刑警寧澤壶愤,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布淑倾,位于F島的核電站,受9級(jí)特大地震影響公你,放射性物質(zhì)發(fā)生泄漏踊淳。R本人自食惡果不足惜假瞬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一陕靠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脱茉,春花似錦剪芥、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至榜田,卻和暖如春益兄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箭券。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工净捅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辩块。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓蛔六,卻偏偏與公主長(zhǎng)得像荆永,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子国章,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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