线程协作(通信) 16


    线程协作:

  • 应用场景:1. 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。

                             2. 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库的产品

                                  被消费者取走为止。

                             3. 如果仓库放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次

                                  放入产品为止。

           这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。

  • java提供了几个方法解决线程之间的通信问题:

 

     注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IIIegaIMoitorStateException。

  • 解决式:1. 并发协作模型“生产者/消费者”--->管程法

                         2. 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)

                         3. 消费者:负责生产数据的模块(可能是方法,对象,线程,进程)

                         4. 缓冲区:消费者不能直接使用生产者的数据,它们之间有个“缓冲区”

                    生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据。

          第二种:并发协作模型“生产者/消费者模式”-->信号灯法(trun , Flase)

 

 同步机制:

package Runnable1;
/*
线程通信:使用两个线程打印1-100,线程1,线程2 交替打印
涉及三个方法:
wait():一旦执行此方法,当前线程就进入阻塞状态并释放同步监视器(锁)
notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的线程
notifyAll:一旦执行此方法,就会唤醒所有被wait的线程
说明:
只能是出现在同步代码块或者同步方法中,调用者必须是同步代码块或者同步方法中同步监视器。
否则,会出现异常
三个方法是定义在java.lang.Object类中
面试题:sleep和wait的异常
相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
不同点:1. 两个方法声明的位置不同,Thread类中声明sleep(),Object列类中声明wait()
      2. 调用的要求不同:sleep()可以在任何需要的场景下调用,wait()必须使用同步代码块或同步方法中
      3. 关于是否是否同步监视器:如果两个方法都使用在同步代码块或者同步方法中,sleep()不会释放锁,wait()会释放锁
*/
class Number implements Runnable
{
 private int numeber=1;
   @Override
   public void run()
  {
       while (true)
      {
           synchronized (this) //快捷键ctrl+alt+t
          {
               notify();
               if (numeber<=100)
              {
                   try {
                       Thread.sleep(100);
                  } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                  }
                   System.out.println(Thread.currentThread().getName()+":"+numeber);
                   numeber++;


                   try {
                       //使得调用如下wait()方法的线程进入阻塞状态 wait会释放锁
                       wait();
                  } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                  }
              }else {
                   break;
              }
          }
      }
  }
}


public class NumberCommunitionTest
{
   public static void main(String[] args)
  {
       Number number=new Number();
       Thread thread1=new Thread(number,"线程1");
       Thread thread2=new Thread(number,"线程2");
       thread1.start();
       thread2.start();
  }


}
​----------------------------
package Runnable1;
/*
线程通信的应用:生产者消费者问题
分析:
是否是多线程问题?是,生产者线程,消费者线程
是否有共享数据?是店员(或产品)
如何来解决线程安全问题?同步机制:三种方法
是否涉及线程的通信?是
*/

class Clerk//
{
   private int productCount=0;

//生产产品
   //在这两个线程中一个时间内只有一个线程去跑
   public synchronized void ProduceProduct()
  {
       if (productCount<20)
      {
           productCount++;
           System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
           //只要生产了一个产品,就唤醒消费者
           notify();
      }else {//不生产
           try {
               wait();
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
      }
  }
//消费产品
   public synchronized void consumeProduct()
  {
       if (productCount>0)
      {
           System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
           productCount--;
           notify();//只要消费一个产品,就把生产者唤醒生产
      }else {//小于零
           //等待
           try {
               wait();
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
      }
  }
}
class Producer extends Thread//生产者
{
   private Clerk clerk;//共享数据

   public Producer(Clerk clerk)
  {
       this.clerk = clerk;
  }

   @Override
   public void run()
  {
       System.out.println(getName()+":开始生产产品........");//因为继承Thread.currentThread().可以省略
       while (true)
      {
           try {
               Thread.sleep(10);
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
           clerk.ProduceProduct();//调用clerk类的方法
      }
  }
}
class Consumers extends Thread//消费者
{
   private Clerk clerk;//共享数据

   public Consumers(Clerk clerk)
  {
       this.clerk = clerk;
  }

   @Override
   public void run()
  {
       System.out.println(getName()+":开始消费产品........");//因为继承Thread.currentThread().可以省略
       while (true)
      {
           try {
               Thread.sleep(20);
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
           clerk.consumeProduct();//调用clerk类的方法
      }
  }
}

public class ProductTest
{
   public static void main(String[] args) {
       Clerk clerk=new Clerk();
       Producer p1=new Producer(clerk);
       p1.setName("生产者1");
       Consumers c1=new Consumers(clerk);
       c1.setName("消费者1");
       p1.start();
       c1.start();

  }

}

模拟生产者和消费者的案例:

  1. 创建馒头类(模拟共用的数据)
public
class
ManTou {
 
    int id;
     
    public ManTou(int id)
    {
        this.id=id;
    }
     
     
    public String toString()
    {
        return "ManTou[id="+this.id+"]";
    }
     
}
创建篮子(模拟生产者和消费者的缓冲区) 
public
class
Basket {
     
    int index=0;
     
    ManTou[] manTous=new ManTou[6];
     
    //往篮子中放馒头
    public synchronized void push(ManTou manTou)
    {
        //篮子满了 需要等待
         if(index==6)
            try {
                this.wait();
            catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         this.notify(); //唤醒的是pop中的wait方法 一旦放馒头了 则说明篮子中有馒头了 取馒头的方法不用等了
         this.manTous[index]=manTou;
         index++;  
    }  
    //从篮子中取馒头
    public synchronized ManTou pop()
    {
        if(index==0)
            try {
                this.wait();
            catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         
        this.notify(); //唤醒的是push中的wait方法  一旦取馒头了 则说明篮子有空位了 放馒头的行为就不用等了
        index--;
        return this.manTous[index];
    }
}
创建生产者(模拟产生或分配数据的对象)
public
class
Producer 
extends
Thread{
     
      Basket basket=null;
       
      public Producer(Basket basket)
      {
          this.basket=basket;
      }
       
      //执行任务
      public void run()
      {
          for(int i=1;i<=20;i++)
          {
              ManTou manTou=new ManTou(i);
               
              basket.push(manTou);
               
              System.out.println("生产了"+manTou);
               
              try {
                this.sleep(200);
            catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
          }
           
      } 
}
创建消费者(处理数据)

 public class Consumer extends Thread{

     
    Basket basket=null;
     
    public Consumer(Basket basket)
    {
        this.basket=basket;
    }
    //执行任务
    public void run()
    {
        for(int i=1;i<=20;i++)
        {
           ManTou manTou=   this.basket.pop();
            
           System.out.println("消费了"+manTou);
            
           try {
            this.sleep(500);
        catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        }
         
    }
 
}
 
public class Test {
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        Basket basket=new Basket();
         
        Producer producer=new Producer(basket);
         
        Consumer consumer=new Consumer(basket);
         
        producer.start();
         
        consumer.start();  
         
    }
 
}
posted @   zjw_rp  阅读(139)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示