等待唤醒机制与生产消费者模型

等待·唤醒机制 与生产消费者模型

等待唤醒机制:线程之间的通信

通过保证在临界区上由多个线程的相互排斥从,线程同步完全可以避免资源冲突的发生,但是有时还需要线程之间的相互协作。使用条件便于线程之间的通信。

线程间通信三大问题:

1,分工

2,互斥

3,同步

重点: 有效利用资源 :对其状态的判断

1.机制

**同步队列:**所有尝试获取锁对象失败的线程保存在这个队列中,排队等待获取锁;

**等待队列:**已经拿到锁的线程,在等待其他资源时,主动释放锁,置入该等待队列中等待被唤醒。

队列先进先出:引出“公平锁

2. synchronized锁 等待唤醒 实现机制

2.1 机制

没有资源–>消费者线程唤醒生产者线程–>消费者线程等待–>生产者线程生产–>资源生成–>修改资源状态为“有”;

有资源–>生产者线程唤醒消费者线程–>生产者线程等待–>消费者线程消费–>消费资源–>修改资源状态为“没有”;

Object下的方法:

2.2 wait():

1.wait()方法的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程置入“等待队列”中,并且在wait()所在的代码处停止执行,直到被唤醒或被中断。

2.wait()方法只能在同步块或同步代码中使用。如果调用wait()方法时必须持有锁,会抛出异常。

3.重载

public final native void wait(Long timeout):

等待一段时间,若没有唤醒,自行唤醒,并将自己置于同步队列中

4.注意!!!:

一旦线程调用待条件的await()

请解释sleep()与wait()的区别:

1.sleep()是Thread类中定义的方法,到了一定的时间后该线程自动唤醒,不会释放对象锁;

2.wait()是Object类中定义的方法,要想唤醒必须使用notify()、notifyAll()方法才能唤醒锁,会释放对象锁。

2.3 notify():

会在等待队列中任意唤醒一个线程,将其置入同步队列尾部,排队获取锁。

唤醒对象获取了锁,只有其执行完毕后才会释放锁。

2.4 notifyAll():

会把等待队列中的所有线程都唤醒,都置于同步队列中。

2.5 注意:

1.wait()与notify()必须由同一个锁调用

2.wait()与notify()都属于Object类的方法;

3.wait()方法与notify()必须在同步代码块或同步方法中使用,首先必须获取锁。

3. Lock锁 等待唤醒 实现机制

3.1 Condition 简介

线程间的通信通常用到Object类中的 wait(),notify()这两个方法以及他们的重载。这两个方法是与对象监视器配合完成线程间的等待/通知机制,而Condition 与Lock配合完成等待唤醒机制,前者是java底层级别的,后者是语言级别的,具有更高的可控制性和扩展性。

两者的功能特性差异

1,Condition能够支持不响应中断,而通过使用Object方式不支持

2,Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个

3,Condition能够支持超时时间的设置,而Object不支持

3.2 Condition实现原理

3.2.1 等待队列

创建一个Condition对象使用lock.newCondition(),这个方法实际上会呢哇出一个ConditionObject对象,该类是AQS的一个内部类,AQS内部维护了一个同步队列所有获取锁失败的线程会尾插中这个同步队列中,而,condition内部也是同样的维护了一个等待队列,所有调用condition.await方法的线程会加入到等待队列中,并且线程状态转换为等待状态。

多次使用newCondition() 创建对象,会创建出多个等待队列,即**一个Lock可以持有多个等待队列。**而之前的Object对象监视器只能拥有一个同步队列和一个等待队列

3.3 方法

3.3.1 await()

释放Lock锁,将线程置于等待队列阻塞

一旦线程调用带条件的await(),线程就进入等待状态等待恢复信号。如果忘记对状态调用signal()或者signalAll(),那么线程就会永远等待下去。

重载:await()

3.3.2 signal()与singalAll

调用signal()或signalAll可以将等待队列中的等待时间最长的节点移动到同步队列中。

3.4 注意

状态由Lock对象创建。为了调用任意条件(如await()、signal()和signalAll()),必须首先获取锁。如果没有获取锁就调用这些方法,会抛出IllegalMonitorStateException异常。

4 .到底使用sunchronized 还是Lock?

1.若无特殊的应用场景,推荐使用synchronized,其使用方便(隐式的加减锁),并且由于synchronized是JVM层面的实现,在之后的JDK还有对其优化的空间。

2.若要使用公平锁、读写锁、超时过去锁等特殊场景、才会考虑使用Lock

5 . 生产-消费者模型

1,资源类:设置资源属性;设置资源状态;

​ 资源状态分为两类

​ 1)true:有资源

​ 2)false:没有资源

2,生产者:生产者类是一个线程类,实现Runnable接口,设置线程任务:生产资源

3,消费者:消费者类 是一个线程类,实现Runnable接口,设置线程任务:消费资源

4,测试类:包含mian方法,生产方法和消费方法。

实例 实现:

package PrCoTest;

/**
 * @Name:  生产消费者模型
 * @Author:ZYJ
 * @Date:2019-06-10-19:46
 * @Description:  模拟线程间通信
 */

/**
 * 测试类,包含main方法,创建生产消费者线程
 */
public class PrCoDemo {
    public static void main(String[] args) throws InterruptedException {
      Goods goods =new Goods();
      Produce produce = new Produce(goods);
      Consumer consumer = new Consumer(goods);
      Thread produceThread= new Thread(produce,"生产者线程");
      Thread consumerThread = new Thread(consumer,"消费者线程");
      produceThread.start();
      produceThread.sleep(1000);
      consumerThread.start();
    }
}

/**
 * 资源类
 * 设置资源属性 资源状态
 * 同步生产方法
 * 同步消费方法
 */
class Goods{
    private  String goodsName;//商品名称
    private  int count; //商品数量

    /**
     * 生产方法
     * 对资源的状态进行判断 若有资源  调用wait()等待
     *                     若无资源  生产
     * @param goodsName
     */
    public synchronized void set(String goodsName) throws InterruptedException {
        //有商品
        if(count>0){
            System.out.println("有"+goodsName+",等待消费者消费....");
            wait();
        }
        //没有商品
        this.goodsName=goodsName;
        this.count=count+1;
        System.out.println("生产"+toString());
        //生产完毕,唤醒消费者线程消费
        notify();
    }

    /**
     * 消费方法
     * 对资源状态进行判断  若有资源,消费 ,消费后将状态置为 没有资源
     *                    若没有资源 调用wait() 等待
     */
    public synchronized void get() throws InterruptedException {
        //没有商品
        if(count==0){
            System.out.println(goodsName+"没有卖光了,等一等...");
            wait();
        }
        //有商品
        this.count=this.count-1;
        Thread.sleep(1000);
        System.out.println("买买买"+toString());
        //消费完毕,唤醒生产者线程生产
        notify();
    }

    @Override
    public String toString(){
        return "Goods[ goodsName="+goodsName+",库存为"+count+"]";
    }
}


/**
 * 生产者类
 * 创建资源对象变量,有一个带参的构造方法为这个资源变量赋值
 * 覆写run()方法,设置线程任务
 *
 */
class Produce implements Runnable{
    private  Goods goods;

    Produce(Goods goods) {
        super();//子类没有无参构造时,显示super().
        this.goods = goods;
    }
    //设置线程任务
    @Override
    public void run() {
        try {
            this.goods.set("房子");//调用生产方法
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 消费者类
 * 创建资源对象变量,有一个带参的构造方法为这个资源变量赋值
 * 覆写run()方法,设置线程任务
 */
class Consumer implements  Runnable{
    private  Goods goods;

    Consumer(Goods goods) {
        super();
        this.goods = goods;
    }

    //设置线程任务
    @Override
    public void run() {
        try {
            this.goods.get();//调用消费方法
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

实例 多生产与多消费

package PrCoSTest;

/**
 * @Name:  生产消费者模型  :多生产与多消费
 * @Author:ZYJ
 * @Date:2019-06-11-11:37
 * @Description:    模拟线程间通信
 */

import java.util.ArrayList;
import java.util.List;

/**
 * 测试类
 */
public class PrCosDemo {
    public static void main(String[] args) {
        Goods goods = new Goods();
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Producer producer = new Producer(goods);
            Thread produceThread = new Thread(producer);
            produceThread.setName("生产者线程"+i);
            threadList.add(produceThread);
        }
        for(int i=0;i<6;i++){
            Consumer consumer = new Consumer(goods);
            Thread consumerThread = new Thread(consumer);
            consumerThread.setName("消费者线程"+i);
            threadList.add(consumerThread);
        }
        for(Thread thread : threadList){
            thread.start();
        }
    }
}

/**
 * 资源类 :
 *   设置资源属性与状态
 *   创建生产与消费方法
 */
class Goods{
    private  String goodsName;
    private int count;

    /**
     * 生产方法
     * @param goodsName
     */
    public synchronized void set(String goodsName) throws InterruptedException {
        //有商品 等待消费
        while (this.count>0){
            wait();
        }
        this.goodsName=goodsName;
        this.count=count+1;
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName());
        System.out.println("生产"+toString());
        System.out.println("=========================");
        //生产完毕 唤醒消费者线程
        notifyAll();
    }

    /**
     * 消费方法
     */
    public synchronized void get() throws InterruptedException {
        //当没有资源时
        while (this.count==0){
            //等待
            wait();
        }
        //有资源消费
        this.count=this.count-1;
        Thread.sleep(1000);//模拟网络延迟
        System.out.println(Thread.currentThread().getName());
        System.out.println("消费"+toString());
        System.out.println("===================");
        //消费完毕,唤醒生产者线程生产
        notifyAll();
    }

    //覆写toString()方法
    @Override
    public  String toString(){
        return "Goods["+goodsName+",库存为:"+count+"]";
    }

}

/**
 * 生产者线程
 */
class Producer implements Runnable{
    private Goods goods;

    Producer(Goods goods) {
        super();
        this.goods = goods;
    }

    //设置线程任务
    @Override
    public void run() {
        while (true){
            try {
                //调用生产方法
                this.goods.set("mac一台");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 消费者线程
 */
class Consumer implements Runnable{
    private  Goods goods;

    Consumer(Goods goods) {
        super();
        this.goods = goods;
    }

    @Override
    public void run() {
        while (true) {
            try {
                this.goods.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值