zoukankan      html  css  js  c++  java
  • 多线程之线程间通信

    什么是等待通知机制

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

  • 相关阅读:
    (ZOJ 3329) One Person Game (概率DP)
    python爬虫之json数据处理
    1034 Head of a Gang 图的遍历,map使用
    1030 Travel Plan Dijkstra+dfs
    vs C++ scanf 不安全
    1021. Deepest Root DFS 求最长无环路径
    1013. Battle Over Cities 用dfs计算联通分量
    无法解析的外部符号
    PAT DFS,BFS,Dijkstra 题号
    1004 Counting Leaves 对于树的存储方式的回顾
  • 原文地址:https://www.cnblogs.com/cg-ww/p/14552471.html
Copyright © 2011-2022 走看看