Java学习day48-线程的通信,生产者与消费者

一、线程通信

1.wait()与notify()和notifyAll()

  ①wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问。

  ②notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待。

  ③notifyAll():唤醒正在排队等待资源的所有线程结束等待。

2.Java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.lllegalMonitorStateException异常。

3.这三个方法只能用在有同步锁的方法或者代码块中。

二、wait()方法

1.在当前线程中调用方法:对象名.wait()

2.使当前线程进入等待(某对象)状态,直到另一线程对该对发出notify(或notifyAll)为止。

3.调用方法的必要条件:当前线程必须具有该对象监控权(加锁)。

4.调用此方法后,当前线程将释放对象监控权,然后进入等待。

5.在当前线程被notify后,要重新获得监控权,然后从断点处继续代码的执行。

三、notify()/notifyAll()

1.在当前线程中调用方法:对象名.notify()

2.功能:唤醒等待该对象监控权的一个线程。

3.调用方法的必要条件:当前线程必须具有该对象监控权(加锁)。

package day20;

public class Test2 {
    public static void main(String[] args){
        //定义账户对象
        Acount a = new Acount();
        Acount a1 = new Acount();
        
        //多线程对象
        User u_weixin = new User(a,2000);
        User u_zhifubao = new User(a,2000);
        
        Thread weixin = new Thread(u_weixin,"微信");
        Thread zhifubao = new Thread(u_zhifubao,"支付宝");
        
        weixin.start();
        zhifubao.start();
    }
}

class Acount{
    public static int money = 3000;
    public synchronized void drawing5(int m,Acount a){
        synchronized(a){//表示通过方法的参数传递进来的对象的代码块,被加了synchronized同步锁
        //不同的对象就有了不同的同步锁
        String name = Thread.currentThread().getName();
        
        //需求:如果是微信操作的,先不执行,等支付宝操作,支付宝操作完,微信再继续操作
        if(name.equals("微信")){
            try {
                a.wait();//当前线程进入等待的阻塞状态
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        if(money < m){
            System.out.println(name + "操作,账户金额不足:" + money);
        }else{
            System.out.println(name + "操作,账户原有金额:" + money);
            System.out.println(name +"操作,取款金额:" + m);
            System.out.println(name +"操作,取款操作:原金额" + money + "-" +"取款金额" + m);
            money = money - m;
            System.out.println(name +"操作,账户取款后余额:" + money);
        }
        
        if(name.equals("支付宝")){
            a.notify();//唤醒当前优先级最高的线程,进入就绪状态
//            a.notifyAll();//唤醒当前所有的线程,进入就绪状态
        }
    }
    }
}

class User implements Runnable{
    Acount acount;
    int money;
    public User(Acount acount,int money){
        this.acount = acount;
        this.money = money;
    }

    public void run() {
        
        acount.drawing5(money,acount);
    }
}

打印结果:

 

四、经典例题:生产者/消费者问题

生产者(Productor)将产品交给店员(Cleak),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,等店中有产品了再通知消费者来取走产品。

这里可能出现两个问题:

1.生产者比消费者块时,消费者会漏掉一些数据没有取到。

2.消费者比生产者快时,消费者会取走相同的数据。

package day20;
/**
 * 生产者和消费者
 * */
public class Test3 {
    public static void main(String[] args){
        final Clerk c = new Clerk();
        
        //消费时不生产,生产时不消费
        
        //生产者
        new Thread(new Runnable(){

            public void run() {
                // TODO Auto-generated method stub
                synchronized (c) {
                    while(true){//无限循环代表无限的生产次数
                        if(c.productNum == 0){//产品数为0,开始生产
                            System.out.println("产品数为0,开始生产");
                            while(c.productNum < 4){
                                c.productNum++;//增加产品
                                System.out.println("库存:" + c.productNum);
                            }
                            System.out.println("产品数为:" + c.productNum + ",结束生产");
                            
                            c.notify();//唤醒消费者
                        }else{
                            try {
                                c.wait();//让生产者线程等待
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
            
        },"生产者").start();
        
        //消费者
        new Thread(new Runnable(){

            public void run() {
                // TODO Auto-generated method stub
                synchronized (c) {
                    while(true){//无限循环代表无限的消费次数
                        if(c.productNum == 4){//产品数为4,开始消费
                            System.out.println("产品数为4,开始消费");
                            while(c.productNum > 0){
                                c.productNum--;//消费产品
                                System.out.println("库存:" + c.productNum);
                            }
                            System.out.println("产品数为:" + c.productNum + ",结束消费");
                            
                            c.notify();//唤醒生产者线程
                        }else{
                            try {
                                c.wait();//让消费者者线程等待
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
            
        },"消费者").start();
    }
}

class Clerk{
    public static int productNum = 0;
    
}

部分打印结果:

 

posted @ 2020-04-05 21:53  苏胖胖  阅读(217)  评论(0)    收藏  举报