多線程之線程間通信


什么是等待通知機制

在單線程中,要執行的操作需要滿足一定條件才能執行,可以把這個操作放在if語句塊中。

在多線程編程中,可能A線程的條件沒有滿足只是暫時的,稍后其他的線程B可能會更新條件使得A線程的條件得以滿足,可以將A線程暫停,直到它的條件得到滿足之后再將A線程喚醒

Atomic{
 while(條件不成立)
 {
 等待
 }
 條件滿足后,當前線程被喚醒
}

等待通知機制的實現

object類中的Wait方法可以使當前線程的代碼暫停執行,直到接到通知或者被中斷為止

注意:

(1)wait方法只能再同步代碼塊中由鎖對象調用

(2)調用wait方法,當前線程會釋放鎖

public class Text16_5 {
    public static void main(String[] args) throws InterruptedException {
        String text="hello";
        System.out.println("同步前代碼塊");
        synchronized (text)
        {
            System.out.println("同步代碼塊開始");
            text.wait();
            System.out.println("同步代碼塊結束");
        }
        System.out.println("全部結束");
    }
}

image-20210316211333325

因為調用了鎖對象的wait方法,會釋放鎖對象,處於等待的狀態,沒有被喚醒就會一直等待下去。

object類的notify方法可以喚醒線程,該方法也必須同步在代碼塊中,由鎖對象調用,沒有使用鎖對象調用wait/notify會報出IIegalMonuitorStateExeption異常,如果由多個等待的線程,notify方法只能喚醒其中的一個,在同步代碼塊中調用notify方法后,並不會立即釋放鎖對象,需要等當前同步代碼塊執行完后才會釋放鎖對象,一般將notify放在同步代碼塊最后。

synchronized(鎖對象)
{
  //執行修改保護條件的代碼
  //喚醒其他線程
  鎖對象.notify();
}
public class TextNotify {
    public static void main(String[] args) throws InterruptedException {
        String text="hello";
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (text)
                {
                    System.out.println("同步代碼塊開始");
                    try {
                        text.wait();//線程等待 
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("同步代碼塊結束");
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (text)
                {
                    System.out.println("線程開始喚醒");
                    text.notify();
                    System.out.println("線程結束喚醒");
                }
            }
        });
        t1.start();//開啟t1線程 t1等待
        Thread.sleep(3000);//睡眠3秒 確保t1處於等待狀態
        t2.start();//喚醒t1線程
    }
}

image-20210316214002434

notify不會立即釋放鎖對象

案例:

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

public class NotifyText2 {
    public static void main(String[] args) throws InterruptedException {
        List<String> strings=new ArrayList<>();
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (strings)
                {
                    System.out.println("線程1開始等待");
                    try {
                        strings.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("線程1被喚醒");
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
               synchronized (strings)
               {
                   for (int i = 0; i <10 ; i++) {
                       strings.add("data"+i);
                       System.out.println("線程2添加了"+(i+1));
                       if(strings.size()==5)
                       {
                           strings.notify();
                           System.out.println("線程2被喚醒");
                       }
                   }
               }
            }
        });
        t1.start();
        Thread.sleep(1000);
        t2.start();
    }
}

線程2的代碼還沒有執行完畢,鎖沒有立即釋放依然在執行,需要等到所有代碼塊全部執行完畢才釋放

image-20210316220423289

interrupt會中斷線程的等待

public class InterruptText {
    private static  final String name=new String();
    public static void main(String[] args) throws InterruptedException {

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (name)
                {
                    try {
                        System.out.println("同步代碼塊開始");
                        name.wait();
                        System.out.println("同步代碼塊結束");
                    } catch (InterruptedException e) {
                        System.out.println("wait被中斷"+e);
                    }
                }
            }
        });
        t1.start();
        Thread.sleep(2000);
        t1.interrupt();
    }
}

原來鎖對象需要執行完同步代碼塊才能釋放鎖對象,在執行過程如果遇到異常也會導致線程終止,釋放鎖對象。調用wait方法也會釋放鎖對象。

image-20210317202003190

notify與notifyAll的區別

notify一次只能喚醒一個,如果有多個線程都在等待,只能隨機喚醒其中的一個,想要喚醒所有等待線程需要調用notifyAll。

public class InterruptText {
    private static  final String name=new String();
    public static void main(String[] args) throws InterruptedException {
        String str=new String();
        NotifyAll notifyAll=new NotifyAll(str);
        NotifyAll notifyAl2=new NotifyAll(str);
        NotifyAll notifyAll3=new NotifyAll(str);
        notifyAll.setName("線程一");
        notifyAl2.setName("線程二");
        notifyAll3.setName("線程三");
        notifyAll.start();
        notifyAl2.start();
        notifyAll3.start();
        Thread.sleep(2000);//休眠兩秒
        synchronized (str)
        {
            //str.notify();只能隨機喚醒一個
            str.notifyAll();//喚醒全部線程
        }
    };
     static class NotifyAll extends Thread
    {
        private    String name;
        private  NotifyAll(String name)
        {
            this.name=name;
        }
                @Override
                public void run() {
                    synchronized (name)
                    {
                        try {
                            System.out.println(Thread.currentThread().getName()+"同步代碼塊開始");
                            name.wait();
                            System.out.println(Thread.currentThread().getName()+"同步代碼塊結束");
                        } catch (InterruptedException e) {
                            System.out.println("wait被中斷"+e);
                        }
                    }
                }

    }
}

image-20210317221240524

如果只調用一次notify()之惡能喚醒其中的一個線程,其他等待線程依然處於等待狀態,就錯過了通知信號,這種現象稱之為信號丟失。

wait(Long)的使用

帶有參數的Wait(Long)方法,在指定時間內沒有操作會被自動喚醒


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM