多线程之等待通知机制

什么是等待通知机制

在单线程中,要执行的操作需要满足一定条件才能执行,可以把这个操作放在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)方法,在指定时间内没有操作会被自动唤醒

Tags: