生产者消费者模式和阻塞队列

生产者消费者模式

是一种设计模式,用于解决两个点(线程、进程、服务器)之间数据通信的协调问题。

生产数据的点叫生产者,使用数据的点叫消费者,生产者和消费者可能存在速度不一致的情况。生产者速度过快,消费者消费速度慢,会浪费大量资源;反过来,消费者速度快,生产者速度慢,消费者浪费时间取等待。

过程:

  1. 设置缓冲区,设定临界值
  2. 生产者生产数据,存入缓冲区,如果缓冲区达到临界值,生产者就等待;否则就通知消费者进行消费
  3. 消费者从缓冲区取出数据,如果缓冲区空了,消费者等待;否则就通知生产者继续生产

解决的问题:

1. 解耦,生产者和消费者之间加入缓冲区,不需要直接调用
2. 忙闲不均,协调生产者和消费者之间的速度
3. 节约资源,减少生产者资源的浪费,和消费者等待的时间

等待和通知机制

锁对象:

  • 同步方法
    • 静态方法 : 类.class
    • 实例方法: this

同步代码块
写在synchronized括号中的对象

可以通过锁对象调用Object类的方法:

  • wait() 让当前线程等待,直到被通知
  • wait(long) 让线程等待,直到被通知或时间结束
  • notify()  随机唤醒一个线程
  • notifyAll()  唤醒所有线程

注意:如果不是锁对象调用上面的方法会出现异常IllegalMonitorStateException

/**
 * 等待和通知案例
 */
public class WaitDemo {

    //测试等待
    public synchronized void testWait(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"执行了"+i);
            if(i == 5){
                System.out.println(Thread.currentThread().getName()+"进行等待");
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //测试通知
    public synchronized void testNotify(){
        this.notifyAll();
    }

    public static void main(String[] args) {
        WaitDemo demo = new WaitDemo();
        //启动线程测试等待
        for(int i = 0;i < 5;i++) {
            new Thread(() -> {
                demo.testWait();
            }).start();
        }
        //5秒后主线程通知
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        demo.testNotify();
    }
}

使用Lock实现

Lock的等待和通知

Condition 类可以提供锁对象类似的方法

创建:

Condition condition = lock.newCondition();

等待和通知:

await()  让线程等待

signal() 通知线程

signalAll()  通知所有线程

/**
 * 包子铺
 */
public class BaoziStore2 {

    //创建锁对象
    private Lock lock = new ReentrantLock();
    //获得Condition对象
    private Condition condition = lock.newCondition();
    //临界值
    public static final int MAX_SIZE = 50;
    //缓冲区
    private List<Baozi> baozis = new ArrayList<>();

    //做包子
    public void makeBaozi(){
        try {
            //上锁
            lock.lock();
            //如果缓冲区满了,生产者等待
            if (baozis.size() == MAX_SIZE) {
                System.out.println("包子铺满了,生产者" + Thread.currentThread().getName() + "等待");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                //通知所有消费者
                condition.signalAll();
            }
            if (baozis.size() < MAX_SIZE) {
                //创建包子对象,存入缓冲区
                Baozi baozi = new Baozi(baozis.size());
                baozis.add(baozi);
                System.out.println("生产者" + Thread.currentThread().getName() + "做了" + baozi + ",总数:" + baozis.size());
            }
        }finally {
            //释放锁
            lock.unlock();
        }
    }

    //拿包子
    public void takeBaozi(){
        try {
            lock.lock();
            //如果缓冲区空了,消费者等待
            if (baozis.size() == 0) {
                System.out.println("包子铺空了,消费者" + Thread.currentThread().getName() + "等待");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                //通知生产者生产
                condition.signalAll();
            }
            if (baozis.size() > 0) {
                //从缓冲区拿出一个包子
                Baozi baozi = baozis.remove(0);
                System.out.println("消费者" + Thread.currentThread().getName() + "拿了" + baozi + ",总数:" + baozis.size());
            }
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        BaoziStore2 store = new BaoziStore2();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                store.makeBaozi();
            }
        }).start();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 10; j++) {
                    store.takeBaozi();
                }
            }).start();
        }
    }
}

阻塞队列

是一种特殊的集合,会自动阻塞对队列添加或删除数据的线程,也可以自动通知

BlockingQueue接口

put(T)  添加数据到队尾,到临界值阻塞

T take  从队头取出并删除数据,到空时阻塞

常用实现类:

ArrayBlockingQueue  数组结构的阻塞队列

LinkedBlockingQueue 链表结构的阻塞队列

SynchronousQueue 同步机制的队列

        //创建阻塞队列 临界值为5
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
        new Thread(()->{
            for(int i = 0;i < 100;i++){
                System.out.println(Thread.currentThread().getName()+"添加"+i+",size:" + queue.size());
                try {
                    queue.put(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(()->{
            for(int i = 0;i < 100;i++){
                try {
                    Integer data = queue.take();
                    System.out.println(Thread.currentThread().getName()+"取出"+data+",size:" + queue.size());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

使用阻塞队列实现

/**
 * 包子铺
 */
public class BaoziStore3 {

    //临界值
    public static final int MAX_SIZE = 50;
    //缓冲区 使用阻塞队列实现
    private BlockingQueue<Baozi> baozis = new ArrayBlockingQueue<>(MAX_SIZE);

    //做包子
    public void makeBaozi(){
        //创建包子对象,存入缓冲区
        try {
            Baozi baozi = new Baozi(baozis.size());
            baozis.put(baozi);
            System.out.println("生产者" + Thread.currentThread().getName() + "做了" + baozi + ",总数:" + baozis.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //拿包子
    public void takeBaozi(){
        //从缓冲区拿出一个包子
        try {
            Baozi baozi = baozis.take();
            System.out.println("消费者" + Thread.currentThread().getName() + "拿了" + baozi + ",总数:" + baozis.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        BaoziStore3 store = new BaoziStore3();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                store.makeBaozi();
            }
        }).start();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 10; j++) {
                    store.takeBaozi();
                }
            }).start();
        }
    }
}

ThreadLocal

线程局部变量,也是解决线程同步问题的一种方式

锁机制  多个线程排队访问一个资源  以时间换空间

ThreadLocal 多个线程并发访问自己独立的资源 以空间换时间

使用ThreadLocal

public class ThreadLocalDemo {

    //线程共享的变量
    static volatile int value = 1;

    public static void main(String[] args) {
        //线程本地变量
        ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
            //设置初始值
            @Override
            protected Integer initialValue() {
                return 1;
            }
        };
        for(int j = 0;j < 3;j++) {
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    value++;
                    local.set(local.get() + 1);
                }
                System.out.println(Thread.currentThread().getName()+" value=" + value);
                System.out.println(Thread.currentThread().getName()+" local=" + local.get());
            }).start();
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" value="+value);
        System.out.println(Thread.currentThread().getName()+" local="+local.get());
    }
}

ThreadLocal的原理

每个线程内部有Map集合ThreadLocalMap,获得数据时候返回当前线程的Map集合,局部变量是保存到当前线程的Map中的

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值