文档章节

Java并发编程初级篇(十六):Lock+Condition实现生产者消费者问题

阿拉德大陆的魔法师
 阿拉德大陆的魔法师
发布于 2016/11/25 15:53
字数 1059
阅读 39
收藏 1

之前我们在“Java并发编程初级篇(十二):使用wait和notify生产者消费者问题”,已经使用Java提供的synchronized关键字和wait(),notify(),notifyAll()方法实现过来生产者消费者问题。Java API还为我们提供了锁的解决方案。

使用锁解决阻塞要用到Condition,它是通过Lock.newCondition()来获得的。就像wait()和notify()必须在synchronized块内一样,Condition.await()和Condition.singialAll()方法也必须在Lock.lock()和Lock.unlock()内执行。

代码示例:

首先我们实现一个数据缓冲区,缓冲区中定义了maxSize变量来代表缓冲区大小,LinkedList来模拟缓冲区。然后添加一把锁,并用这把锁来新建两个Condition:producer(控制生产者挂起和唤醒)和consumer(控制消费者挂起和唤醒)。当生产者发现缓冲区满的情况下调用producer.await()挂起,等待消费者消费数据后调用producer.singialAll()方法来唤醒,并重新判断缓冲区状态。当消费者发现缓冲区空的情况下调用consumer.await()挂起,等待生产者向缓冲区中放入数据并调用consumer.singialAll()方法唤醒并重新判断缓冲区状态。

public class DataBuffer {
    private int maxSize;
    private LinkedList<Date> buffer;

    private Lock lock;
    private Condition producer;
    private Condition consumer;

    public DataBuffer(int maxSize) {
        this.maxSize = maxSize;
        buffer = new LinkedList<Date>();

        lock = new ReentrantLock();

        producer = lock.newCondition();
        consumer = lock.newCondition();
    }

    public void put() {
        try {
            lock.lock();

            while (buffer.size() == this.maxSize) {
                producer.await();
            }

            buffer.add(new Date());

            System.out.printf("%s: Add one %s. Buffer size is %d.\n",
                    Thread.currentThread().getName(),
                    buffer.peekLast(),
                    buffer.size());

            consumer.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void get() {
        try {
            lock.lock();

            while (buffer.size() == 0) {
                consumer.await();
            }

            System.out.printf("%s: Get one %s. Buffer size is %d.\n",
                    Thread.currentThread().getName(),
                    buffer.pollFirst(),
                    buffer.size());

            producer.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

创建两个线程用于模拟生产者和消费者

public class Producer implements Runnable {
    private DataBuffer buffer;

    public Producer(DataBuffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        buffer.put();
    }
}

public class Consumer implements Runnable {
    private DataBuffer buffer;

    public Consumer(DataBuffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        buffer.get();
    }
}

创建两个任务类线程启动10个生产者和消费者,生产者生产速度>消费者消费速度。

public class ProducerTask implements Runnable {
    private DataBuffer buffer;

    public ProducerTask(DataBuffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            new Thread(new Producer(buffer), "Producer-" + i).start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ConsumerTask implements Runnable {
    private DataBuffer buffer;

    public ConsumerTask(DataBuffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            new Thread(new Consumer(buffer), "Consumer-" + i).start();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

主方法类中启动两个任务类线程,模拟生产者和消费者,因为生产者速度快,最终会导致生产者处于阻塞状态。

public class Main {
    public static void main(String[] args) {
        DataBuffer buffer = new DataBuffer(5);

        new Thread(new ProducerTask(buffer), "ProducerTask").start();
        new Thread(new ConsumerTask(buffer), "ConsumerTask").start();
    }
}

日志,因为缓冲区大小为5,所以当缓冲区内容达到5个之后,生产者进入阻塞状态,并等待消费者消费数据后被唤醒并向缓冲区中插入数据。

Producer-0: Add one Fri Nov 25 15:36:21 CST 2016. Buffer size is 1.
Consumer-0: Get one Fri Nov 25 15:36:21 CST 2016. Buffer size is 0.
Producer-1: Add one Fri Nov 25 15:36:22 CST 2016. Buffer size is 1.
Producer-2: Add one Fri Nov 25 15:36:23 CST 2016. Buffer size is 2.
Producer-3: Add one Fri Nov 25 15:36:24 CST 2016. Buffer size is 3.
Producer-4: Add one Fri Nov 25 15:36:25 CST 2016. Buffer size is 4.
Consumer-1: Get one Fri Nov 25 15:36:22 CST 2016. Buffer size is 3.
Producer-5: Add one Fri Nov 25 15:36:26 CST 2016. Buffer size is 4.
Producer-6: Add one Fri Nov 25 15:36:27 CST 2016. Buffer size is 5.
Consumer-2: Get one Fri Nov 25 15:36:23 CST 2016. Buffer size is 4.
Producer-7: Add one Fri Nov 25 15:36:31 CST 2016. Buffer size is 5.
Consumer-3: Get one Fri Nov 25 15:36:24 CST 2016. Buffer size is 4.
Producer-8: Add one Fri Nov 25 15:36:36 CST 2016. Buffer size is 5.
Consumer-4: Get one Fri Nov 25 15:36:25 CST 2016. Buffer size is 4.
Producer-9: Add one Fri Nov 25 15:36:41 CST 2016. Buffer size is 5.
Consumer-5: Get one Fri Nov 25 15:36:26 CST 2016. Buffer size is 4.
Consumer-6: Get one Fri Nov 25 15:36:27 CST 2016. Buffer size is 3.
Consumer-7: Get one Fri Nov 25 15:36:31 CST 2016. Buffer size is 2.
Consumer-8: Get one Fri Nov 25 15:36:36 CST 2016. Buffer size is 1.
Consumer-9: Get one Fri Nov 25 15:36:41 CST 2016. Buffer size is 0.

 

© 著作权归作者所有

共有 人打赏支持
阿拉德大陆的魔法师
粉丝 23
博文 91
码字总数 83019
作品 0
西城
程序员
Java面试:投行的15个多线程和并发面试题

本文由ImportNew -一杯哈希不加盐 翻译自dzone。欢迎加入翻译小组。转载请见文末要求。 多线程和并发问题已成为各种 Java 面试中必不可少的一部分。如果你准备参加投行的 Java 开发岗位面试,...

ImportNew
08/23
0
0
Java中高级面试必问之多线程TOP50(含答案)

以下为大家整理了今年一线大厂面试被问频率较高的多线程面试题,由于本人的见识局限性,所以可能不是很全面,也欢迎大家在后面留言补充,谢谢。 1、什么是线程? 2、什么是线程安全和线程不安...

老道士
08/28
0
0
Java并发基础你需要知道的基础知识

多线程和并发编程是Java里面的核心内容,通常有以下一些概念需要重点掌握。 线程; 锁; 同步器; 并发容器和框架; Java并发工具类; 原子操作类; Executor框架(执行机制); 并发基础概念 ...

异步社区
05/30
0
0
【转】15个顶级Java多线程面试题及回答

Java 线程面试问题   在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多关于多线程的问题。在投资银行业务...

一只死笨死笨的猪
2014/09/30
0
0
15个顶级Java多线程面试题及回答

Java 线程面试问题 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多关于多线程的问题。在投资银行业务中多...

LCZ777
2014/05/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

java基础知识,小栗子

来操作一下数组.....注意带参数的变长数组的使用. package com.avatus;import java.util.Random;import java.util.Scanner;public class Main { public static void main(St...

Oh_really
22分钟前
2
0
SSO单点登录PHP简单版

  前面做了一个新项目,需要用户资源可以需要共享。由于之前没有做过这样的东西,回家之后,立马网站百度“单点登录”。帖子很多,甄别之后,这里列几篇认为比较有营养。   http://blog...

slagga
今天
2
0
Java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。 本文参考java 泛型详解、Java中的泛型方法、 java泛型详解 1 概述 泛型在j...

hensemlee
今天
2
0
Annotation注解详细介绍

目录介绍 1.Annotation库的简单介绍 2.@Nullable和@NonNull 3.资源类型注释 4.类型定义注释 5.线程注释 6.RGB颜色纸注释 7.值范围注释 8.权限注释 9.重写函数注释 10.返回值注释 11.@Keep注释...

潇湘剑雨
今天
2
0
一步步编写自己的PHP爬取代理IP项目(二)

这一章节我们正式开展我们的爬虫项目,首先我们先要知道哪个网站能获取到免费代理IP,目前比较火的有西刺代理,快代理等,这里我们拿西刺代理作为例子。 这里就是一个个免费的IP地址以及各自...

NateHuang
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部