使用JUC并发工具包的Lock和Condition,实现生产者和消费者问题中的有界缓存

JDK5.0之前,用java实现生产者和消费者的唯一方式就是使用synchronized内置锁和wait/notify条件通知机制。JDK5.0之后提供了显示锁Lock和条件队列Condition,与内置锁和内置条件队列相对应,但是显示的锁和条件队列,功能更强大,更灵活。此外JDK5.0之后还提供了大量很有用的并发工具类,如BlockingQueue等,基于这些数据结构,能够方便、快速、高效的构建自己应用需要的效果。这里我们简单使用下显示锁和条件队列,来模拟有界缓存的实现,功能类似于JDK内置的阻塞队列BlockingQueue。

生产者和消费者问题,这里不再介绍了,网上有很多资料。这里提一个概念:条件谓词。条件谓词就是使某个依赖状态的操作能够成功的前提条件。对于有界缓存来说,只有当缓存不为空,消费者才能get数据,否则消费者必须等待;只有当缓存未满,生产者才能put数据,否者生产者必须等待。对于生产者线程来说,它的条件谓词是"缓存未满";对于消费者来说,它的条件谓词是"缓存非空"。为什么要提到条件谓词呢?因为如果我们找到了所有的条件谓词,才能够清楚线程之间的关系和依赖,才能实现正确的等待和唤醒。《java并发编程实践》一书14.2节很好地阐了锁、条件队列、条件谓词这3者之间的联系。

首先我们使用最原始的synchronized和wait/notify机制,实现生产者和消费者问题中的有界缓存,代码如下:

package waitnotify;

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

public class InSideLock
{
	// 保护共享数据的锁
	private Object lockObj = new Object();

	// 缓冲池
	private List<String> pools = null;

	// 最大容量
	private int CAPACITY = 0;

	public InSideLock(int capacity)
	{
		this.pools = new ArrayList<String>(capacity);
		this.CAPACITY = capacity;
	}

	public void put(String e) throws InterruptedException
	{
		synchronized (lockObj)
		{
			while (pools.size() == CAPACITY)
			{
				// 调用wait,必须要持有锁,而且必须在while循环中测试条件
				lockObj.wait();
			}

			pools.add(e);
			lockObj.notifyAll();
		}
	}

	public String get() throws InterruptedException
	{
		synchronized (lockObj)
		{
			while (pools.size() == 0)
			{
				lockObj.wait();
			}

			String value = pools.remove(0);

			lockObj.notifyAll();

			return value;
		}
	}
}

使用显示锁和条件队列代码如下:

package waitnotify;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class VisibleLock
{
	// 保护共享数据的锁
	private Lock mainLock = new ReentrantLock();

	// 消费者等待的非空谓词
	private Condition notEmpty = mainLock.newCondition();

	// 生产者等待的非满谓词
	private Condition notFull = mainLock.newCondition();

	private List<String> pools = null;

	private int CAPACITY = 0;

	public VisibleLock(int capacity)
	{
		this.pools = new ArrayList<String>(capacity);
		this.CAPACITY = capacity;
	}

	public void put(String e) throws InterruptedException
	{
		mainLock.lock();

		try
		{
			while (pools.size() == CAPACITY)
			{
				notFull.await();
			}

			pools.add(e);

			// 使用条件队列的显示API,特别注意这里不能使用notify
			notEmpty.signalAll();
		} finally
		{
			mainLock.unlock();
		}

	}

	public String get() throws InterruptedException
	{
		mainLock.lock();

		try
		{
			while (pools.size() == 0)
			{
				notEmpty.await();
			}

			notFull.signalAll();

			return pools.remove(0);
		} finally
		{
			mainLock.unlock();
		}
	}
}

下面是测试生产者和消费者的代码:

package waitnotify;

public class Test
{
	public static void main(String[] args) throws Exception
	{
		final InSideLock pc = new InSideLock(10);

		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run()
			{
				while (true)
				{
					try
					{
						System.out.println("get=" + pc.get());
					} catch (InterruptedException e)
					{
					}
				}

			}
		});

		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run()
			{
				while (true)
				{
					try
					{
						long time = System.currentTimeMillis();
						System.out.println("put=" + time);

						pc.put(time + "");
					} catch (InterruptedException e)
					{
					}
				}

			}
		});

		t1.start();
		t2.start();

	}
}

可以发现使用内置锁和内置条件队列,与使用显示锁和显示条件队列代码几乎是一样的。

使用JUC并发工具包的Lock和Condition,实现生产者和消费者问题中的有界缓存,布布扣,bubuko.com

时间: 2024-12-20 22:59:08

使用JUC并发工具包的Lock和Condition,实现生产者和消费者问题中的有界缓存的相关文章

用ReentrantLock和Condition实现生产者和消费者模式

前面一篇文章<wait.notify应用场景(生产者-消费者模式)>是一种生产者消费者模式实现,今晚这是Lock方式实现,下面是源码: 生产者代码: /** * 生产者 * * @author tangquanbin * @date 2018/12/18 22:10 */ public class Producer implements Runnable { /** * 产品容器 */ private List<Integer> container; private Lock lo

并发无锁队列学习(单生产者单消费者模型)

1.引言 本文介绍单生产者单消费者模型的队列.根据写入队列的内容是定长还是变长,分为单生产者单消费者定长队列和单生产者单消费者变长队列两种.单生产者单消费者模型的队列操作过程是不需要进行加锁的.生产者通过写索引控制入队操作,消费者通过读索引控制出队列操作.二者相互之间对索引是独享,不存在竞争关系.如下图所示: 2.单生产者单消费者定长队列 这种队列要求每次入队和出队的内容是定长的,即生产者写入队列和消费者读取队列的内容大小事相同的.linux内核中的kfifo就是这种队列,提供了读和写两个索引.

Java并发(三)使用显式的Lock和Condition对象

在之前的Java并发(一)wait()与notifyAll()一文中的例子中,我们使用了wait()和notifyAll()来模拟了给汽车打蜡和抛光的情景.在JavaSE5中,还提供了java.util.concurrent.locks.Condition对象供我们使用.你可以在Condition上调用await()来挂起一个任务.当外部条件发生变化,意味着某个任务应该继续执行时,你可以通过调用signal()来通知这个任务,或者调用signalAll()来唤醒所有在这个Condition上被其

JAVA 并发编程-线程同步通信技术(Lock和Condition)(十)

在之前的博客中已经介绍过线程同步通信技术<JAVA 并发编程-传统线程同步通信技术(四)>,上篇是使用的synchronized,wait,notify来实现,今天我们使用的是Lock和Condition,下面我们结合两者对比来学习. 简单的Lock锁应用: /** * 简单Lock的应用 * @author hejingyuan * */ public class LockTest { public static void main(String[] args) { new LockTest

【Java并发工具类】Lock和Condition

前言 Java SDK并发包通过Lock和Condition两个接口来实现管程,其中Lock用于解决互斥问题,Condition用于解决同步问题.我们需要知道,Java语言本身使用synchronized实现了管程的,那么为什么还在SDK中提供另外一种实现呢?欲知为何请看下文. 下面将先阐述再造管程的理由,然后详细介绍Lock和Condition,最后再看实现同步机制时是选择synchronized还是SDK中的管程. 再造管程的理由 Java本就从语言层面实现了管程,然而后面又在SDK中再次现

5、Java并发编程:Lock

Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?这个问题将在下面进行阐述.本文先从synchronized的缺陷讲起,然后再讲述java.util.concurrent.locks包

【Java线程】锁机制:synchronized、Lock、Condition

http://www.infoq.com/cn/articles/java-memory-model-5  深入理解Java内存模型(五)——锁 http://www.ibm.com/developerworks/cn/java/j-jtp10264/  Java 理论与实践: JDK 5.0 中更灵活.更具可伸缩性的锁定机制 http://blog.csdn.net/ghsau/article/details/7481142 1.synchronized 把代码块声明为 synchronize

Java并发编程之---Lock框架详解

Java 并发开发:Lock 框架详解 摘要: 我们已经知道,synchronized 是Java的关键字,是Java的内置特性,在JVM层面实现了对临界资源的同步互斥访问,但 synchronized 粒度有些大,在处理实际问题时存在诸多局限性,比如响应中断等.Lock 提供了比 synchronized更广泛的锁操作,它能以更优雅的方式处理线程同步问题.本文以synchronized与Lock的对比为切入点,对Java中的Lock框架的枝干部分进行了详细介绍,最后给出了锁的一些相关概念. 一

并发编程 16—— Lock

Java并发编程实践 目录 并发编程 01—— ConcurrentHashMap 并发编程 02—— 阻塞队列和生产者-消费者模式 并发编程 03—— 闭锁CountDownLatch 与 栅栏CyclicBarrier 并发编程 04—— Callable和Future 并发编程 05—— CompletionService : Executor 和 BlockingQueue 并发编程 06—— 任务取消 并发编程 07—— 任务取消 之 中断 并发编程 08—— 任务取消 之 停止基于线