zoukankan      html  css  js  c++  java
  • java基础知识回顾之java Thread类学习(七)--java多线程通信等待唤醒机制(wait和notify,notifyAll)

    1.wait和notify,notifyAll

    • wait和notify,notifyAll是Object类方法,因为等待和唤醒必须是同一个锁,不可以对不同锁中的线程进行唤醒,而锁可以是任意对象,所以可以被任意对象调用的方法,定义在Object基类中。
    • wait()方法:对此对象调用wait方法导致本线程放弃对象锁,让线程处于冻结状态,进入等待线程的线程池当中。wait是指已经进入同步锁的线程,让自己暂时让出同步锁,以便使其他正在等待此锁的线程可以进入同步锁并运行,只有其它线程调用notify方法或者notifyAll方法后,才能唤醒线程池中等待的线程。
    • notify()方法:唤醒线程池中的任意一个线程。
    • notifyAll方法:唤醒线程池中的所有线程。

    2.线程间通信多个线程操作同一个资源,但是操作的动作不同,任务不同。

    3.等待唤醒机制:操作共享数据的不同动作,一个存入,一个取出;当输入一个的时候,另一个要取出。先让输入线程等待,等输出线程取出后,再唤醒输入线程。

    下面看一个例子:需求是要求两个线程操作Resource资源类中的成员变量name,sex,要求一个线程往共享资源类中存入一个姓名和一个姓别,即存入一个资源对象,另一个线程就取出资源类中的对象。即存入一个,就要取出,资源类中对象为空的时候,就再存入一个。这个例子可以用等待唤醒机制解决。

    思路:这里在线程代码里面设计一个姓名为peter,性别为man;另一个姓名为“李明”,性别为“男”。通过模2进行切换。输入线程选择操作这连个对象。

    class Res{
        String name;
        String sex;
        boolean flag = false;
    }
    
    class Input implements Runnable{
        Object obj = new Object();
        private Res r ;
    
        public Input(Res r){//通过构造方法初始化资源,把Res传递进来,一构造对象,资源被初始化
            this.r = r;
        }
    
        public void run(){
            int x= 0;
            while(true){
                synchronized(r){//同步代码块,锁为r
                    if(r.flag){//如果资源里面有一个姓名和名字了,input线程就冻结,释放执行权,释放执行资格,释放锁
                        try {
                            r.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                        if(x == 0){//切换x,通过模2进行切换
                            r.name="peter";
                            r.sex="man";
                        }else{
                            r.name="李明";
                            r.sex="男男男男";
                        }
                        x = (x+1)%2;//取模运算,模2=0,取名字为Mike,性别为man,不等于0,则取小红,女女女女
                        r.flag = true;//当存入一个,标志变为true,输入线程进入等待,处于冻结状态,等待输出线程输出
                        r.notify();//唤醒线程池中的其它线程,唤醒第一个等待的输出线程
                }
            }
        }
        
    }
    class OutPut implements Runnable{
        Object obj = new Object();
        private Res r;
        public OutPut(Res r){
            this.r  = r;
        }
        public void run() {
            
            while(true){
                synchronized(r){
                    if(!r.flag)
                        try {
                            r.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        //如果r.flag=true,说明资源Res里面有一个东西,输出里面名字和性别
                        System.out.println(r.name+ "......"+r.sex);
                        r.flag=false;//当输出一个,标志改为false,这时输出线程进入线程池进行等待,直到被唤醒
                        r.notify();//唤醒输入线程,导致当前线程等待
                }
            }
        }
        
    }
    public class ThreadorThreadSpeak {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            //创建资源
            Res r = new Res();//比喻为煤资源
            //创建任务
            Input in = new Input(r);
            OutPut out = new OutPut(r);
            //创建线程
            Thread t1 = new Thread(in);//输入线程
            Thread t2 = new Thread(out);//输出线程
            //开启线程
            t1.start();
            t2.start();
        }
    
    }

    优化上面的代码,把同步代码块中的代码封装成同步函数

    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+"...+...."+sex);
            flag = false;
            notify();
        }
    }
    
    
    //输入
    class Input implements Runnable
    {
        Resource r ;
        Input(Resource r)
        {
            this.r = r;
        }
        public void run()
        {
            int x = 0;
            while(true)
            {
           //取2模进行切换
    if(x==0) { r.set("peter","nan"); } else { r.set("李明","男男男男"); } x = (x+1)%2; } } } //输出 class Output implements Runnable { Resource r; Output(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ResourceDemo3 { public static void main(String[] args) { //创建资源。 Resource r = new Resource(); //创建任务。 Input in = new Input(r); Output out = new Output(r); //创建线程,执行路径。 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //开启线程 t1.start(); t2.start(); } }

    输出:

    peter......man
    李明......男男男男
    peter......man
    李明......男男男男
    peter......man

     
  • 相关阅读:
    Windows Server 2003下ASP.NET无法识别IE11的解决方法
    SQL Server2005中使用XML-数据类型、查询与修改
    连接SQLServer时提示“但是在登录前的握手期间发生错误。 (provider: SSL Provider, error: 0
    无法将类型为 excel.applicationclass 的 com 强制转换为接口类型 的解决方法。
    C# WinForm使用Aspose.Cells.dll 导出导入Excel/Doc 完整实例教程
    技巧 获取电脑硬件信息 -转发
    浏览器无需下载插件 解决网页长截图的小技巧 -转发
    note 9 列表、时间复杂度、排序
    note 8 字符串
    note 7 递归函数
  • 原文地址:https://www.cnblogs.com/200911/p/3901732.html
Copyright © 2011-2022 走看看