`
coolxing
  • 浏览: 874187 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
9a45b66b-c585-3a35-8680-2e466b75e3f8
Java Concurre...
浏览量:97288
社区版块
存档分类
最新评论

状态依赖的类--JCIP C14.1读书笔记

阅读更多

[本文是我对Java Concurrency In Practice C14.1的归纳和总结.  转载请注明作者和出处,  如有谬误, 欢迎在评论中指正. ] 

java类库中包含许多状态依赖的类: 其中的某些方法只有满足特定的前置条件才能继续, 比如BlockingQueue的take方法, 只有队列不为空时take方法才能返回.

状态依赖的操作一般如下:

void blockingAction() {
	申请锁
	while(前置条件不满足) {
		释放锁
		重新获取锁
	}
	执行操作
	释放锁
}
 

BaseBoundedBuffer是一个普通抽象类, 它对put和take方法的实现是有缺陷的: 没有在put方法执行前判断缓冲区是否已满, 也没有在take方法执行之前判断缓冲区是否为空. 其代码如下:

public abstract class BaseBoundedBuffer<V> {
	private final V[] buf;
	private int tail;
	private int head;
	private int count;

	@SuppressWarnings("unchecked")
	protected BaseBoundedBuffer(int capacity) {
		this.buf = (V[]) new Object[capacity];
	}

	protected synchronized final void doPut(V v) {
		buf[tail] = v;
		if (++tail == buf.length) {
			tail = 0;
		}
		++count;
	}

	protected synchronized final V doTake() {
		V v = buf[head];
		buf[head] = null;
		if (++head == buf.length) {
			head = 0;
		}
		--count;
		return v;
	}

	public synchronized final boolean isFull() {
		return count == buf.length;
	}

	public synchronized final boolean isEmpty() {
		return count == 0;
	}
} 

本文将使用多种方式为doPut和doTake方法增加前置判断.

 

通知调用方前置条件判断失败

GrumpyBoundedBuffer是BaseBoundedBuffer的子类, 并向外提供put和take方法. 调用put方法时, 如果缓冲区已满, 将抛出BufferFullException异常. 调用take方法时, 如果缓冲区为空, 将抛出BufferEmptyException异常:

public class GrumpyBoundedBuffer<V> extends BaseBoundedBuffer<V> {
	public GrumpyBoundedBuffer(int size) {
		super(size);
	}

	public synchronized void put(V v) throws BufferFullException {
		if (isFull())
			throw new BufferFullException();
		doPut(v);
	}

	public synchronized V take() throws BufferEmptyException {
		if (isEmpty())
			throw new BufferEmptyException();
		return doTake();
	}
} 

GrumpyBoundedBuffer实现起来很简单, 但是这样的类很难使用: 调用方需要捕获并处理异常. 例如调用take方法时需要这样:

public void invocaton() {
	while (true) {
		try {
			V item = buffer.take();
			// use item 
			break;
		} catch (BufferEmptyException e) {
			// 当抛出BufferEmptyException异常时, 说明buffer为空. 调用方睡眠一段时间后再进行尝试
			Thread.sleep(SLEEP_GRANULARITY);
		}
	}
} 

这样的调用方式是令人难以忍受的, 而且sleep的时间SLEEP_GRANULARITY不好确定: 如果设定的太短, 将白白消耗CPU资源. 如果设定的太长, 则程序的响应性不好, 也有可能错过前置条件满足的时刻.

 

内部处理重试逻辑

既然由调用方处理异常并重试是不可取的, 那么SleepyBoundedBuffer类改为在内部处理重试逻辑:

public class SleepyBoundedBuffer<V> extends BaseBoundedBuffer<V> {
	private static final long SLEEP_GRANULARITY = 10;

	public SleepyBoundedBuffer(int size) {
		super(size);
	}

	public void put(V v) throws InterruptedException {
		while (true) {
			synchronized (this) {
				if (!isFull()) {
					doPut(v);
					return;
				}
			}
			// 释放锁后sleep一段时间再进行重试
			Thread.sleep(SLEEP_GRANULARITY);
		}
	}

	public V take() throws InterruptedException {
		while (true) {
			synchronized (this) {
				if (!isEmpty()) {
					return doTake();
				}
			}
			// 释放锁后sleep一段时间再进行重试
			Thread.sleep(SLEEP_GRANULARITY);
		}
	}
}

SleepyBoundedBuffer相比于GrumpyBoundedBuffer具有很大的进步: 不需要在调用方进行重试. SleepyBoundedBuffer易于使用, 但是sleep的时间仍然不好确定, 需要在响应性和CPU消耗间权衡. 

 

前置条件满足时唤醒线程

BoundedBuffer试着解决SleepyBoundedBuffer中的问题: 当前置条件不满足时将线程挂起, 并等待前置条件满足时由其他线程唤醒, 这样就不需要权衡sleep的时间了:

public class BoundedBuffer<V> extends BaseBoundedBuffer<V> {
	public BoundedBuffer(int size) {
		super(size);
	}

	public synchronized void put(V v) throws InterruptedException {
		// 当缓冲区已满时将线程挂起, 等待其他线程唤醒
		// 不给唤醒之后再次判断缓冲区是否已满
		while (isFull()) {
			wait();
		}
		doPut(v);
		// 操作完成后唤醒其他线程
		notifyAll();
	}

	public synchronized V take() throws InterruptedException {
		// 当缓冲区为空时将线程挂起, 等待其他线程唤醒
		// 被唤醒之后再次判断缓冲区是否为空
		while (isEmpty()) {
			wait();
		}
		V v = doTake();
		// 操作完成后唤醒其他线程
		notifyAll();
		return v;
	}
} 

BoundedBuffer已经比较完美了, 相比于SleepyBoundedBuffer, 其具有更好的响应性, 更高的CPU效率以及更少的上下文切换.

 

1
0
分享到:
评论

相关推荐

    libselinux-devel-2.5-14.1.el7.x86_64.rpm

    离线安装包,亲测可用

    redis-server:5.0.14.1 windows版本

    标题提到的是Redis的特定版本——5.0.14.1,专为Windows操作系统设计。这个版本的Redis是经过编译和配置,可以直接在Windows环境下运行的。 在描述中,我们看到这个压缩包提供了一个“解压即用”的版本,这意味着...

    windows-redis_5.0.14.1

    windows-redis_5.0.14.1 windows环境下的redis5版本 新增加了一些特性 新增加的Stream(流)数据类型,这样redis就有了6大数据类型,另外五种是String(字符串),Hash(哈希),List(列表),Set(集合)及Zset...

    lineage-14.1-20190920-UNOFFICIAL-apollo.7z.001

    这是hdx8.9的rom lineage-14.1-20190920-UNOFFICIAL-apollo.zip,由于上传限制,分了两个压缩包,用7-zip解压即可lineage-14.1-20190920-UNOFFICIAL-apollo.zip

    Redis-x64-5.0.14.1.msi

    RDB 在指定时间间隔保存整个数据库状态,AOF 记录所有写操作命令,系统重启时重新执行这些命令恢复数据。 6. **主从复制**:通过复制,可以创建一个或多个副本服务器,当主服务器出现问题时,可以无缝切换到副本,...

    Redis-x64-5.0.14.1

    这个名为"Redis-x64-5.0.14.1"的压缩包是Redis针对Windows操作系统的64位版本,版本号为5.0.14.1。在Windows上运行Redis可能与Linux环境有所不同,但仍然提供了相同的核心功能。 1. **Redis的特性**: - **内存...

    Redis-x64-5.0.14.1 缓存程序

    在Windows系统中,Redis-x64-5.0.14.1是Redis的一个版本,专为64位操作系统设计。这个压缩包文件包含了在Windows环境下运行Redis所需的所有组件。 首先,我们来深入了解Redis的核心特性: 1. **键值存储**:Redis...

    Redis稳定版 Redis-x64-5.0.14.1.zip

    RDB会在指定时间间隔创建数据库的快照,而AOF则记录所有写操作,确保在服务器重启后能恢复到最新的状态。 3. **主从复制**: Redis支持主从复制,可以将主服务器的数据实时同步到多个从服务器,实现数据备份和负载...

    Redis-x64-5.0.14.1 for Windows

    之前项目中要使用window下的redis。找了好久都只有Redis-x64-3.2.100的,后来发现有更高...这里附件里面包括Redis-x64-5.0.14.1 for Windows版本 与及 原始下载地址。如果有新的版本的话,可以到在原始下载地址找到。

    boost-1-67-0-msvc-14.1-64.exe

    boost_1_67_0-msvc-14.1-64版本安装包,可以直接安装免编译,直接安装即可使用

    struts2-spring-2.5.14.1.jar

    1. **依赖注入**:Spring的DI可以用于初始化Struts2的动作类,这样可以避免硬编码依赖,使得组件更易于测试和重构。 2. **Action实例管理**:Spring可以管理Struts2 Action的生命周期,包括单例或多例模式的选择,...

    Redis-x64-5.0.14.1.zip

    在你提供的压缩包"Redis-x64-5.0.14.1.zip"中,包含的是Redis 5.0.14的64位Windows版本。解压这个文件后,你会得到以下关键组件: 1. **redis-server.exe**:这是Redis服务器的执行文件,用于启动Redis服务。当你...

    最新版windows Redis-x64-5.0.14.1.zip

    本压缩包"windows Redis-x64-5.0.14.1.zip"包含了最新版的Windows 64位Redis服务器,具体版本号为5.0.14.1,其核心亮点在于提供稳定、高效的服务。 Redis的特点包括但不限于以下几点: 1. **内存存储**:Redis是一...

    Redis-x64-5.0.14.1 Windows版

    Redis-x64-5.0.14.1 Windows版

    Redis-x64-5.0.14.1.msi 安装包

    Redis-x64-5.0.14.1.msi 是 Redis 的一个64位Windows安装包,版本为5.0.14。Redis 是一个开源的、高性能的键值存储系统,它常被用作数据库、缓存和消息代理。这个安装包使得在Windows环境下部署Redis变得更加简便。...

    libselinux-utils-2.5-14.1.el7.x86_64.rpm

    离线安装包,亲测可用

    Redis-x64-5.0.14.1软件安装包

    Redis-x64-5.0.14.1软件安装包:windows安装包,双击安装,下一步下一步。。。。。。即可。【Redis】Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存...

    PyPI 官网下载 | django-pipeline-1.1.14.1.tar.gz

    **PyPI 官网下载 | django-pipeline-1.1.14.1.tar.gz** 在Python的世界里,PyPI(Python Package Index)是官方的软件仓库,它为开发者提供了发布和分享Python软件包的平台。`django-pipeline`是一个在PyPI上可下载...

    Location-cleaned IOS11.4至14.1驱动包

    本压缩包"Location-cleaned IOS11.4至14.1驱动包"包含了针对苹果iOS系统的多个特定版本优化的驱动程序,这些版本包括11.4、12.7、13.6、13.7以及14.0和14.1。这个驱动包的特殊之处在于它经过了“location-cleaned”...

Global site tag (gtag.js) - Google Analytics