资讯专栏INFORMATION COLUMN

并发 - 生产者消费者的问题

DTeam / 1686人阅读

摘要:共享锁能被多个线程同时拥有,能被共享的锁。需要和联合使用,它的作用是代替监视器方法,可以通过来休眠唤醒线程。生产者消费产品新建一个线程向仓库中生产产品。

AQS

AbstractQueuedSynchronizer 抽象类
AQS 是 java 中管理 “锁” 的抽象类,锁的许多公共方法都是在这个类中实现。
AQS 是独占锁 (例如,ReentrantLock) 和共享锁 (例如,Semaphore) 的公共父类。
1.1 独占锁
锁在一个时间点只能被一个线程锁占有。根据锁的获取机制,它又划分为 “ 公平锁 ” 和 “ 非公平锁 ”。
1.1.1 公平锁,是按照通过 CLH 等待线程按照先来先得的规则,公平的获取锁;
1.1.2 非公平锁,则当线程要获取锁时,它会无视 CLH 等待队列而直接获取锁。独占锁的典型实例子是 ReentrantLock,此外,ReentrantReadWriteLock.WriteLock 也是独占锁。

1.2 共享锁
能被多个线程同时拥有,能被共享的锁。JUC 包中的 ReentrantReadWriteLock.ReadLock,CyclicBarrier, CountDownLatch 和 Semaphore 都是共享锁。这些锁的用途和原理,在以后的章节再详细介绍。

![图片上传中...]

ReentrantLock

如果采用Lock,必须主动去释放锁;但是发生异常时,不会自动释放锁。
因此一般来说,使用Lock必须在try{}catch{}中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定会被释放,防止死锁。

使用Lock 进行同步的话,形式如下:

Lock lock = ...;
lock.lock(); // 加锁
try{
    // 处理任务
}catch(Exception ex){
}finally{
lock.unlock(); // 释放锁
}

多线程对共享资源进行操作的时候,使用锁是十分必要的
假设有一个生产者类mPro,一个消费者类mCus,每次操作创建一个线程。

mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
mPro.produce(110);

如果不使用独占锁,很可能出现
Thread-0 produce(60) --> size=-60 Thread-4 produce(110) --> size=50 Thread-2 consume(90) <-- size=-60 Thread-1 produce(120) --> size=-60 Thread-3 consume(150) <-- size=-60
因为首先线程1 对size +60, 没有调用println的时候,切换到线程2, 又加了120, 没打印又被切换了,然后连续两次消费了90 和 150再切换到线程1打印,这个时候size就是-60了。

Condition

Condition 接口描述了可能会与锁有关联的条件变量。Condition 需要和 Lock 联合使用,它的作用是代替 Object 监视器方法,可以通过 await(),signal() 来休眠 / 唤醒线程。

比如生产者-消费者问题中,共享资源空的时候,消费者不能再消费;共享资源满的时候,生产者不能再生产;所以Lock 需要借助Condition

private Condition fullCondtion;            // 生产条件
private Condition emptyCondtion;           // 消费条件
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
class Depot{
    private int capacity; // 仓库的容量
    private int size; // 仓库的实际数量
    private Lock lock; //独占锁
    private Condition fullCondition; // 生产条件
    private Condition emptyCondition; // 消费条件

    public Depot(int capacity) {
        this.capacity = capacity;
        this.size = 0;
        this.lock = new ReentrantLock();
        this.fullCondition = lock.newCondition();
        this.emptyCondition = lock.newCondition();
    }
    public void produce(int val){
        lock.lock();
        try{
             // left 表示“想要生产的数量”(有可能生产量太多,需多此生产)
            int left = val; 
            while(left>0){
                // 库存已满时,等待“消费者”消费产品。
                while(size>=capacity)
                {
                    fullCondition.await();
                }
            // 获取“实际生产的数量”(即库存中新增的数量)
            // 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
            // 否则“实际增量”=“想要生产的数量”
            int inc = (size+left)>capacity ? (capacity-size) : left;
            size += inc;
            left -= inc;
            System.out.println(Thread.currentThread().getName()+"want to produce" + val + "val-inc"+left + "actually produce "+ inc+ "now size"+size);
            // 通知“消费者”可以消费了。
            emptyCondition.signal();
            }
        } catch(InterruptedException e) {
        }finally {
                lock.unlock();
        }
}

public void consume(int val){
    lock.lock();
    try{
        // left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
        int left = val;

        while (left > 0) {
        // 库存为0时,等待“生产者”生产产品。
            while (size <= 0)
            {
                emptyCondition.await();
            }
        // 获取“实际消费的数量”(即库存中实际减少的数量)
        // 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
        // 否则,“实际消费量”=“客户要消费的数量”。
        int dec = (size           
               
                                           
                       
                 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64330.html

相关文章

  • Jakartase_多线程 --- 线程并发协作模式(一)产者费者模式

    摘要:一简介多线程环境下,我们经常需要多个线程的并发和协作。这个时候,就需要了解一个重要的多线程并发协作模型生产者消费者模式。对于生产者没有生产产品之前,消费者要进入等待状态。分析不足在生产者消费者问题中,仅有是不够的。 一、简介 多线程环境下,我们经常需要多个线程的并发和协作。 这个时候,就需要了解一个重要的多线程并发协作模型 生产者 / 消费者模式 。 模式简图 showImg(h...

    qianfeng 评论0 收藏0
  • 想进大厂?50个多线程面试题,你会多少?(一)

    摘要:下面是线程相关的热门面试题,你可以用它来好好准备面试。线程安全问题都是由全局变量及静态变量引起的。持有自旋锁的线程在之前应该释放自旋锁以便其它线程可以获得自旋锁。 最近看到网上流传着,各种面试经验及面试题,往往都是一大堆技术题目贴上去,而没有答案。 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员...

    wow_worktile 评论0 收藏0
  • 使用 mixphp 处理其他框架 20% 并发部分

    摘要:如何使用优化高并发场景写库或者耗时计算在的接口中使用消息队列,把要入库的数据写入的类型中。高容错子进程异常奔溃时,主进程将重建子进程。高性能多进程运行,充分利用多个并行计算,性能强劲。 经常在群里听到一些朋友问:TP 的项目怎么迁移到 mixphp 来处理高并发,我通常都是回复需要重写,可是一个开发很久的 TP 项目,代码量巨大,又怎么可能会花大量时间成本来重写呢? 那么为何我们不尝试...

    silvertheo 评论0 收藏0
  • 译:Java中产者费者问题演变

    摘要:生产者消费者问题是一个典型的多进程同步问题。生产者线程开始产生新的元素并将它们存储在缓冲区。否则,生产者线程将会在缓冲区创建一个新元素然后通知消费者。我们建立一个线程池,它将收到两个任务,生产者和消费者的任务。 原文链接:https://dzone.com/articles/th... 作者:Ioan Tinca 译者:liumapp 想要了解更多关于Java生产者消费者问题的演变吗?...

    王伟廷 评论0 收藏0

发表评论

0条评论

DTeam

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<