• day12_多线程间通信


    多线程通信引入:

    InOut2  

    class Resource
    {
         String name;
         String sex;
         boolean flag=true;
    }
    class Input implements Runnable
    {
      private Resource r;
      Input(Resource r)
      {
        this.r=r;
      }
      public void run()
      {  
        int x=0;
        while(true)
          {
    
                   synchronized(r)//也可以用类文件对象Input.class/Output.class
                   {
                        if(r.flag)
                       {
                          if(x==0)
                            {
                             r.name="Mike";
                             r.sex="men";
                             r.flag=false;
                            }
                            else
                             {
                                 r.name="Lili";
                                 r.sex="女女女女女女";
                                 r.flag=false;
                            
                             }
                        x=(x+1)%2;
                       }
                      
                   }
               
               }
          }
    }
    
    class Output implements Runnable
    {
       private Resource r;
       Output(Resource r)
       {
         this.r=r;
       
       }
       public void run()
       {    while(true)
           {
              
                   synchronized(r)
                   {
                    
                      if(!r.flag)
                      {
                        
                         try{Thread.sleep(100);}catch(Exception e){}
                         System.out.println(r.name+"----"+r.sex+"----"+r.flag);
                         r.flag=true;
                        
                      }
                      
                   }
               
           }
       }
    }
    class InOutDemo
    {
        public static void main(String[] args)
        {
          Resource r=new Resource();
          new Thread(new Input(r)).start();
          new Thread(new Output(r)).start();
        }
    }
    /*
    对输出结果分析:
      可能出现:
     一个线程在执行完:
         r.name="Mike";
         r.sex="men";
         下次执行else内容时
         r.name="Lili";
      执行到此,cpu切换到另一线程:
         输出r.name="Lili",r.sex="men";
    */
    
    /*
    解决以上问题采用同步代码块:
    对输出结果分析:
    为什么没有输出Mike--men,Lili--"女女女女"交替?
     这是因为,当一个线程执行完赋值动作后,
      Cpu可能切换到另一个线程进行循环打印
    */

    InOut

    InOutNormal

    多线程通信-等待唤醒机制

    /*
    等待唤醒机制
    比喻:(有助于理解,在毕老师基础上加了点,可能比喻有偏差,仅供参考)
    
    一种"冰棍化了"了游戏,一群小朋友来到一个院子(锁),一个主角(CPU)追一群小朋友(线程),当
    其中一人被追上(执行)时,他喊"冰棍"(wait),主角不能在抓这个人了.
    而其他人在躲闪过程中可以救(notify)这个喊了冰棍的人.
    
    (API:这翻译真心拗口,英语不好吃亏!)
    wait():
    前提:当前线程必须拥有此 对象监视器(锁)。
    该线程发布(放弃)对此监视器(锁)的所有权并等待,直到其他线程通过调用 notify 方法,
    或 notifyAll 方法通知 在此对象的监视器上等待 的线程醒来.
    然后该线程将等到 重新获得对监视器的所有权后 才能继续执行。
    */
    //需求:为了达到赋值一次,输出一次.
    class Resource
    {
         String name;
         String sex;
         boolean flag=false;
    }
    
    
    class Input  implements Runnable 
    {
      private Resource r;
      Input(Resource r)
      {
        this.r=r;
      }
      public void run()
      {  
        int x=0;
        while(true)
          {
    
                   synchronized(r)//也可以用类文件对象Input.class/Output.class
                   {
                         if(r.flag)
                          try{r.wait();}catch(Exception e){}//当已赋值完成,该线程等待
                                                          //wait()方法是Object中的方法,该方法会抛出异常,不能在run上抛出                             
                          if(x==0)                       
                            {
                             r.name="Mike";
                             r.sex="men";
                             
                            }
                            else
                             {
                               r.name="Lili";
                               r.sex="女女女女女女";
                                
                             }
                         r.flag=true;
                     
                         r.notify();//唤醒另一个线程去取
                      
               
                         
                         x=(x+1)%2;
                    }
                      
          }
               
      }
    }
    
    
    class Output implements Runnable
    {
       private Resource r;
       Output(Resource r)
       {
         this.r=r;
       
       }
       public void run()
       {    while(true)
           {
              
                   synchronized(r)
                   {                              
                         if(!r.flag)
                            try{r.wait();}catch(Exception e){}
                         
                         System.out.println(r.name+"----"+r.sex+"----"+r.flag);
                         
                         r.flag=false;
                        r.notify();
                         
                                              
                     
                   }
               
           }
       }
    }
    class InOutDemo2
    {
        public static void main(String[] args)
        {
          Resource r=new Resource();
          new Thread(new Input(r)).start();
          new Thread(new Output(r)).start();
        }
    }
    
    
    
    /*
    注意:
    关于r.wait():必须标识出wait()操作的 线程 所持有的锁(r)
    因为可能有同步嵌套(不同的锁)
    
    r.notify()唤醒的是: r锁上的 等待线程
    
    
    wait(),notify(),notifyAll()
        都使用在同步中,因为要对持有监视器(锁)的线程操作
        所以要使用在同步中,因为只有同步才具有锁
    
    Q3.为什么wait()方法定义在Object类中?
       因为 锁可以是任意对象,任意对象都能调用的方法->定义在Object中
    
    同一个锁上等待的线程,只能被同一个锁上线程notify
    不可以对不同锁中的线程进行唤醒.
    */

    两个线程交替执行

    对以上代码简单优化(同步函数)

    class Resource   
    {
        private String name,sex;
        private boolean flag=false;
        public synchronized void setValue(String name,String sex)
        {
            if(flag)
              try{this.wait();}catch(Exception e){}//this可以省略
            this.name=name;
            this.sex=sex;
            flag=true;
            this.notify();
        }
        public synchronized void printValue()
        {
            if(!flag)
               try{this.wait();}catch(Exception e){}
            System.out.println(this.name+"----"+this.sex);
            flag=false;
            this.notify();
        }
    }
    class Input implements Runnable
    {
     private Resource r;
     Input(Resource r)
     {
       this.r=r;
     
     }
     public void run()
     {
         int x=0;
         while (true)
         {
            
            if(x==0)
              r.setValue("mike","man");
            else
               r.setValue("Lili","女女女");
            x=(x+1)%2;
         }
    
     
     }
    
    }
    class Output implements Runnable
    {
     private Resource r;
     Output(Resource r)
     {
       this.r=r;
     
     }
     public void run()
     {
         int x=0;
         while (true)
         {
          try{Thread.sleep(100);}catch(Exception e){}
          r.printValue();
         }
    
     
     }
    
    }
    class InOutDemo3
    {
        public static void main(String[] args)
        {
          Resource r=new Resource();
          new Thread(new Input(r)).start();
          new Thread(new Input(r)).start();
        
        }
    }

    生产者-消费者(两个线程生产,两个线程消费)

    class Resource   
    {
        private int count=1;
        private boolean flag=false;
        private String name;
         //Thread-0 Thread-1
        public synchronized void setValue(String name)
        {
            //if(flag)
             while(flag)
              try{this.wait();}catch(Exception e){}//
            
            this.name=name+"---"+count++;
            System.out.println(Thread.currentThread().getName()+"...---生产者生产----..."+this.name);
            flag=true;
            
            //this.notify();//
            this.notifyAll();
        }
        //Thread-2  Thread-3
        public synchronized void printValue()
        {
            //if(!flag)
             while(!flag)
               try{this.wait();}catch(Exception e){}//
          
            System.out.println(Thread.currentThread().getName()+"---消费者消费---"+name);
            flag=false;
          
          //this.notify();//
           this.notifyAll();
        }
    }
    class Producer implements Runnable
    {
     private Resource r;
     Producer(Resource r)
     {
       this.r=r;
     
     }
     public void run()
     {
         int x=0;
         while (true)
          r.setValue("+商品+");
     }
    
    }
    class Consumer implements Runnable
    {
     private Resource r;
     Consumer(Resource r)
     {
       this.r=r;
     
     }
     public void run()
     {
         int x=0;
         while (true)
           r.printValue();
    
         
    
     
     }
    
    }
    class ProCon
    {
        public static void main(String[] args)
        {
          Resource r=new Resource();
          /*
          //这样写也是可以的,虽然是两个生产者/消费者 对象,但
          //都是执行的r对象中的方法.
          new Thread(new Producer(r)).start();
          new Thread(new Producer(r)).start();
          
          new Thread(new Consumer(r)).start();
          new Thread(new Consumer(r)).start();
          
          */
          Producer p=new Producer(r);
          new Thread(p).start();
          new Thread(p).start();
          Consumer c=new Consumer(r);
          new Thread(c).start();
          new Thread(c).start();
          
        }
    }
    /*
    打印结果:可能出现两次生产,只消费一次.生产一次,消费两次...
    分析:(其中一种可能:生产两次,消费一次)
    
    0      1.cpu切换到0线程->执行生产->Thread-0 生产者生产 商品 1->再次执行->
             0线程等待①位置. 
    
    0 1    2.cpu切换到1线程->1线程等待①位置. 
    
    1      3.cpu切换到2线程->执行消费->Thread-2 消费者消费 商品 1->④notify 0->
    1 2       ->再次执行->2线程等待③位置.
    
    1 2 3  4.cpu切换到3线程->3线程等待③位置
    
    
    2 3    5.cpu切换到0线程->从①开始执行->Thread-0 生产者生产 商品 2->②notify 1->
    2 3 0    ->再次执行->0线程等待①位置
    
    
    3 0    6.cpu切换到1线程->从①开始执行->Thread-1 生产者生产 商品 3->②notify 2
    3 0 1    ->再次执行->1线程等待①位置
    
           
           
    0 1     7.cpu切换到2线程->从③开始执行->Thread-2 消费者消费 商品 3->④notify 3
    0 1 2     ->再次执行->2线程等待③位置
    
        
        ......
    另一种可能:先生产即可. 
    
    以上关键在5,6 0线程唤醒1线程,而1线程没有执行flag判断,继续向下执行.
    ->如果把if改成while(flag)/while(!flag)
    ->在第五步0唤醒1->1去等待,0也去等待->全部等待
    
    那么使用notifyAll()全部唤醒
    实际上 本方依然等待,而让对方执行.
    */

                     生产者消费者正常

    生产者-消费者JDK5.0升级(Lock,Condition)

    /*
    JDK1.5版本中提供了多线程的升级解决方案
    (显式的锁机制)
    将同步Synchronized替换成现实的Lock操作
    将Object中的wait,notify,notifyall,替换成了Condition对象.
    该对象可以通过Lock锁进行获取(newCondition())
    在该示例中,实现了本方唤醒对方的操作

    */

    import java.util.concurrent.locks.*;
    class Resource   
    {
        private int count=1;
        private boolean flag=false;
        private String name;
        private Lock lock=new ReentrantLock();//锁对象,ReentrantLock类实现Lock
        private Condition condition_pro=lock.newCondition();    
        private Condition condition_cus=lock.newCondition();                                                           
         //Thread-0 Thread-1                                         
        public  void setValue(String name)throws InterruptedException 
                                                                         
       {     
           lock.lock();
          System.out.println(Thread.currentThread().getName()+"①---"+flag);
           try
           {                                      
              if(flag)
               condition_pro.await();//与此 Condition 相关的锁以原子方式释放
    
          System.out.println(Thread.currentThread().getName()+"②---"+flag);
            this.name=name+"---"+count++;
         System.out.println(Thread.currentThread().getName()+"...---生产者生产----..."+this.name);
            flag=true;
            //condition.signal();
            //condition.singalAll();
             condition_cus.signal();
        System.out.println(Thread.currentThread().getName()+"③---"+flag);
            }
            finally
            { 
             System.out.println("---unlock---");
             lock.unlock();//必须finally,有可能await抛出异常不释放锁
        
            }
        }
        //Thread-2  Thread-3
        public synchronized void printValue()throws InterruptedException
        {
            lock.lock();
       System.out.println(Thread.currentThread().getName()+"④---"+flag);
            try
            {
                 if(!flag)
                    condition_cus.await();
        System.out.println(Thread.currentThread().getName()+"⑤---"+flag);
                 System.out.println(Thread.currentThread().getName()+"---消费者消费---"+name);
                 flag=false;
                 //condition.signal();全等待
                 //condition.signalAll();
                 condition_pro.signal();//唤醒condition_pro对象上的await(其中一个线程)
        System.out.println(Thread.currentThread().getName()+"⑥---"+flag);
            }
    
            finally
            { 
            System.out.println("---UNLOCK---");
              lock.unlock();
            
            }
        
         
        }
    }
    class Producer implements Runnable
    {
     private Resource r;
     Producer(Resource r)
     {
       this.r=r;
     
     }
     public void run()
     {
         int x=0;
         while (true)
          try
          {
             r.setValue("+商品+");
          }
          catch (InterruptedException e)
          {
          }
            
     }
    
    }
    class Consumer implements Runnable
    {
     private Resource r;
     Consumer(Resource r)
     {
       this.r=r;
     
     }
     public void run()
     {
         int x=0;
         while (true)
           try
           {
        
            r.printValue();
           }
           catch (InterruptedException e)
           {
           }
            
    
         
    
     
     }
    
    }
    class ProCon2
    {
        public static void main(String[] args)
        {
          Resource r=new Resource();
          Producer p=new Producer(r);
          new Thread(p).start();
          new Thread(p).start();
          Consumer c=new Consumer(r);
          new Thread(c).start();
          new Thread(c).start();
          
        }
    }
    /*
       //Lock 替代了 synchronized 方法和语句的使用,
      //Condition 替代了 Object 监视器方法的使用
      //wait,notify在同步中需要标识所属的锁
      //因此通过Lock方法获取condition的实例  
      //也就是说Condition 实例实质上被绑定到一个锁上
    */
    
    
    
    /*
    以上仅仅替换了原先的代码(换汤不换药)
    依然存在把本方线程唤醒的可能.
    因此再使用一个condition对象
    可以有多个实例绑到一个锁上,由此体现出新版本优点
    同步中只能有一个对象绑定到一个锁.
    */
    
    /*
    虽然唤醒的是对方线程,但是依然需要while判断标记:
    否则依然出现生产两次,而消费一次:
    cpu切换到0->0生产->在执行->0等待
    cpu切换到2->2消费->唤醒0->在执行->2等待
    cpu切换到1->1生产
    cpu切换到0->0生产
    ...
    */

    interrupt方法:

    /*
    stop 方法已经过时.
    如何停止线程?
    只有一种,run方法结束.
    开启多线程运行,运行代码通常是循环结构
    只要控制住循环,就可以让run方法结束,也就是线程结束.
    
    Thread类中的interrupt方法:(不是终止线程)
    
        当没有指定方式让冻结的线程恢复到运行状态时,
        这时需要对冻结(阻塞)进行清除.强制让线程恢复到就绪状态中来.
        这样就可以操作标记让线程结束.
     清除的根本:使wait方法抛出InterruptedException
    
    
    */
    class StopThread implements Runnable
    {
      private boolean flag=true;
       public synchronized void run()
       {
        while(flag)
           {
                try
                {
                    wait();//当线程处于阻塞状态->不会读取到标记->线程就不会结束
                }          //两个线程均等待
                catch (InterruptedException e)
                {
                   
                System.out.println(Thread.currentThread().getName()+"....Exception...");   
                  flag=false;
                }
                
                
                System.out.println(Thread.currentThread().getName()+"....run...");
           }
       }
       public void changeFlag()
       {
         flag=false;
       
       }
    
    }
    class StopThreadDemo
    {
        public static void main(String[] args)
        {
          StopThread s=new StopThread();
          Thread t1=new Thread(s);
          Thread t2=new Thread(s);
          t1.start();
          t2.start();
          int x=0;
          while(true)
          {
            if(x++==60)
              {
                //s.changeFlag();
                t1.interrupt();
                t2.interrupt();
                break;
              }
           System.out.println(Thread.currentThread().getName()+"...."+x);
          }
          System.out.println(Thread.currentThread().getName()+"....over");
        }
    }

     interrupt

    守护(后台)线程:

    /*
    守护线程:
     当线程被标记为守护线程(后台线程)
     开启,执行与一般线程均无区别,
     但当所有前台线程(未标记)都执行完->守护线程会自动结束
    
    该方法必须在启动线程前调用。
    */
    class StopThread implements Runnable
    {
      private boolean flag=true;
       public synchronized void run()
       {
        while(flag)
           {
            
            System.out.println(Thread.currentThread().getName()+"....run...");
           }
       }
       public void changeFlag()
       {
         flag=false;
       
       }
    
    }
    class setDaemonDemo
    {
        public static void main(String[] args)
        {
          StopThread s=new StopThread();
          Thread t1=new Thread(s);
          Thread t2=new Thread(s);
          t1.setDaemon(true);
          t2.setDaemon(true);//on为true 该线程被标记为守护线程
          t1.start();
          t2.start();
          int x=0;
          while(true)
          {
            if(x++==60)
              {
              
                break;
              }
           System.out.println(Thread.currentThread().getName()+"...."+x);
          }
          System.out.println(Thread.currentThread().getName()+"....over");
        }
    }
    /*
    主线程一执行完,t1,t2自动结束.
    
    */

    守护线程

    为什么在主线程”over”后,守护线程又执行了一会?其实并非这样,之所以在over后又打印了守护线程的输出语句这是因为:可能先执行后被打印

    join方法:

    /*
    join 方法:
    当A线程执行到了B线程的.join方法时,A线程就会等待,
    等B线程都执行完,A才会执行
    
    join可以用来临时加入线程.
    */
    class Demo implements Runnable
    {
     public void run()
     {
      
      for(int i=0;i<70;++i)
      {
       System.out.println(Thread.currentThread().getName()+"....."+i);
      
      
      }
     
     }
    
    }
    class JoinDemo
    {
        public static void main(String[] args) throws Exception
        {
          
          Demo d=new Demo();
          Thread t1=new Thread(d);
          Thread t2=new Thread(d);
          t1.start();
          t2.start(); 
          t1.join();//可以用于临时加入一个线程,让该线程执行完.
                    //当主线程执行到此,把cpu执行权交给t1,主线程会等t1的run()结束后,主线程才能继续执行
                    //而t2是否执行结束不影响主线程
          for(int i=0;i<80;++i)
          {
           System.out.println(Thread.currentThread().getName()+"....."+i);
          
          }
        }
    }

    yield方法:

    class Demo implements Runnable
    {
     public void run()
     {
      
      for(int i=0;i<70;++i)
      {
       System.out.println(Thread.currentThread().getName()+"....."+i);
       Thread.yield();//稍微减缓线程运行,0执行下,1执行下,0执行下...
      
      }
     
     }
    
    }
    class YieldDemo
    {
        public static void main(String[] args) throws Exception
        {
          
          Demo d=new Demo();
          Thread t1=new Thread(d);
          Thread t2=new Thread(d);
          t1.start();
          t2.start(); 
          //t1.setPriority(Thread.MAX_PRIORITY);//使0线程具有更高优先级(1-10,默认为5)
          for(int i=0;i<80;++i)               //cpu执行该线程频率高点
          {
           System.out.println(Thread.currentThread().getName()+"....."+i);
          
          }
        }
    }
  • 相关阅读:
    设计模式之-工厂模式、构造函数模式
    发布订阅小示例
    使用vue,react,angular等框架和不使用框架使用jquery的优缺点
    react优化--pureComponent
    Vue、 React比较
    ORACLE触发器和new、old特殊变量
    mysql的存储过程与自定义函数
    MySQL日期
    php(Personal Home Page)简介,安装和配置(apache服务器使用和配置1)
    话谈html语义化
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/2971997.html
Copyright © 2020-2023  润新知