`
Donald_Draper
  • 浏览: 984418 次
社区版块
存档分类
最新评论

用Semaphore实现对象池

    博客分类:
  • JUC
阅读更多
AtomicInteger解析:http://donald-draper.iteye.com/blog/2359555
锁持有者管理器AbstractOwnableSynchronizer:http://donald-draper.iteye.com/blog/2360109
AQS线程挂起辅助类LockSupport:http://donald-draper.iteye.com/blog/2360206
AQS详解-CLH队列,线程等待状态:http://donald-draper.iteye.com/blog/2360256
AQS-Condition详解:http://donald-draper.iteye.com/blog/2360381
可重入锁ReentrantLock详解:http://donald-draper.iteye.com/blog/2360411
CountDownLatch使用场景:http://donald-draper.iteye.com/blog/2348106
CountDownLatch详解:http://donald-draper.iteye.com/blog/2360597
CyclicBarrier详解:http://donald-draper.iteye.com/blog/2360812
Semaphore 是一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。说白了,Semaphore是一个计数器,在计数器不为0的时候对线程就放行,一旦达到0,那么所有请求资源的新线程都会被阻塞,包括增加请求到许可的线程,也就是说Semaphore不是可重入的。每一次请求一个许可都会导致计数器减少1,同样每次释放一个许可都会导致计数器增加1,一旦达到了0,新的许可请求线程将被挂起。缓存池整好使用此思想来实现的,比如链接池、对象池等。

下面来看一个实例:
package juc.latch;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 信号量实现的对象池
 * @author donald
 * 2017年3月6日
 * 下午9:43:06
 * @param <T>
 */
public class ObjectCache<T> {
	//对象工厂
	public interface ObjectFactory<T> {
		T makeObject();
	}
    //将对象封装节点中,放到一个先进先出的队列中,即对象池
	class Node {
		T obj;
		Node next;
	}

	final int capacity;//线程次容量
	final ObjectFactory<T> factory;
	final Lock lock = new ReentrantLock();//保证对象获取,释放的线程安全
	final Semaphore semaphore;//信号量
	private Node head;
	private Node tail;

	public ObjectCache(int capacity, ObjectFactory<T> factory) {
		this.capacity = capacity;
		this.factory = factory;
		this.semaphore = new Semaphore(this.capacity);
		this.head = null;
		this.tail = null;
	}
    /**
     * 从对象池中,获取对象
     * @return
     * @throws InterruptedException
     */
	public T getObject() throws InterruptedException {
		semaphore.acquire();
		return getObjectFromPool();
	}
    /**
     * 线程安全地从对象池获取对象
     * @return
     */
	private T getObjectFromPool() {
		lock.lock();
		try {
			if (head == null) {
				return factory.makeObject();
			} else {
				Node ret = head;
				head = head.next;
				if (head == null)
					tail = null;
				ret.next = null;// help GC
				return ret.obj;
			}
		} finally {
			lock.unlock();
		}
	}
    /**
     * 线程安全地,将对象放回对象池
     * @param t
     */
	private void putBackObjectToPool(T t) {
		lock.lock();
		try {
			Node node = new Node();
			node.obj = t;
			if (tail == null) {
				head = tail = node;
			} else {
				tail.next = node;
				tail = node;
			}
		} finally {
			lock.unlock();
		}
	}
    /**
     * 将对象放回对象池
     * @param t
     */
	public void putBackObject(T t) {
		putBackObjectToPool(t);
		semaphore.release();
	}
}

ObjectCache描述了一个基于信号量Semaphore的对象池实现。此对象池最多支持capacity个对象,这在构造函数中传入。对象池有一个基于FIFO的队列,每次从对象池的头结点开始取对象,如果头结点为空就直接构造一个新的对象返回。否则将头结点对象取出,并且头结点往后移动。特别要说明的如果对象的个数用完了,那么新的线程将被阻塞,直到有对象被返回回来。返还对象时将对象加入FIFO的尾节点并且释放一个空闲的信号量,表示对象池中增加一个可用对象。实际上对象池、线程池的原理大致上就是这样的,只不过真正的对象池、线程池要处理比较复杂的逻辑,所以实现起来还需要做很多的工作,例如超时机制,自动回收机制,对象的有效期等等问题。这里特别说明的是信号量只是在信号不够的时候挂起线程,但是并不能保证信号量足够的时候获取对象和返还对象是线程安全的,所以在示例中仍然需要锁Lock来保证并发的正确性。将信号量初始化为1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。按此方式使用时,二进制信号量具有某种属性(与很多Lock 实现不同),即可以由线程释放“锁”,而不是由所有者(因为信号量没有所有权的概念)。在某些专门的上下文(如死锁恢复)中这会很有用。上面这段话的意思是说当某个线程A持有信号量数为1的信号量时,其它线程只能等待此线程释放资源才能继续,这时候持有信号量的线程A就相当于持有了“锁”,其它线程的继续就需要这把锁,于是线程A的释放才能决定其它线程的运行,相当于扮演了“锁”的角色。另外同公平锁非公平锁一样,信号量也有公平性。如果一个信号量是公平的表示线程在获取信号量时按FIFO的顺序得到许可,也就是按照请求的顺序得到释放。这里特别说明的是:所谓请求的顺序是指在请求信号量而进入FIFO队列的顺序,
有可能某个线程先请求信号而后进去请求队列,那么次线程获取信号量的顺序就会晚于其后请求但是先进入请求队列的线程。这个在公平锁和非公平锁中谈过很多。
0
0
分享到:
评论

相关推荐

    Java基于Semaphore构建阻塞对象池

    这种机制使得Semaphore可以用于创建阻塞对象池,例如数据库连接池,限制同时使用的连接数量,以防止资源耗尽。 Semaphore提供两种构造方法: 1. `Semaphore(int permits)`:默认是非公平策略,创建一个具有指定许可...

    Java中的Semaphore类最全讲义

    目录: 简介 1.1 并发编程与资源控制 1.2 Semaphore概述 Semaphore的基本用法 2.1 创建Semaphore对象 ...4.2 使用Semaphore实现有界资源池 Semaphore的公平性 5.1 非公平性 5.2 公平性 最佳实践与注意事项

    Java 信号量Semaphore的实现

    在实际应用中,Semaphore可以用于解决多种并发编程问题,例如,控制数据库连接池的并发数量,实现线程池的并发执行,避免资源的竞争等。Semaphore的使用可以提高系统的并发性能,避免线程的竞争,提高系统的可靠性和...

    OS-semaphore.rar_Semaphore_semaphore. windows

    操作系统中的信号量(Semaphore)是一种重要...在《OS信号量机制.ppt》这份资料中,可能包含了更多关于Windows系统下信号量的详细实现、使用示例以及常见问题的解决方法,建议深入学习以提升在多任务环境下的编程能力。

    带你看看Java的锁(二)-Semaphore

    在Java中,Semaphore是通过AbstractQueuedSynchronizer(AQS)来实现的,它是Java并发包`java.util.concurrent`中的一个类。 Semaphore的构造函数可以接受一个整数参数,这个参数代表可用的许可证数量。在上述示例...

    30 限量供应,不好意思您来晚了—Semaphore详解.pdf

    - **资源池管理**:在数据库连接池、线程池等场景中,Semaphore可以用来控制同时使用的连接或线程数量,防止资源过度消耗。 - **保护共享资源**:当多个线程需要共享某个有限资源时,如打印机、文件等,Semaphore...

    Python库 | semaphore-0.4.46.zip

    Semaphore-0.4.46是一个特定版本的Python实现,它提供了对信号量操作的基本接口。在这个版本中,我们可以期待一些关键功能和改进,如: 1. **初始化**:Semaphore对象在创建时可以设置一个初始值,这个值表示可以...

    java线程并发semaphore类示例

    在给出的代码示例中,创建了一个Semaphore对象,初始化为2,意味着最多只有两个线程可以同时运行。`ExecutorService`用于创建一个线程池,执行10个MySemaphore线程。每个MySemaphore线程在运行时首先检查是否有可用...

    commons-pool2-2.5.0-bin.zip

    Apache Commons Pool 2 是一个广泛使用的对象池库,它的最新版本是 2.5.0。对象池在软件设计中是一种优化资源管理的技术,通过复用已创建的对象,避免频繁的创建和销毁过程,从而提高性能。Apache Commons Pool 2 ...

    线程池的VC实现例子

    3. **线程同步与通信**:VC提供了多种线程同步机制,如临界区(`critical_section`)、事件对象(`event`)、互斥对象(`mutex`)等。在线程池中,这些机制用于确保线程安全地访问共享资源,以及协调线程的启动和...

    c++ 面向对象多线程编程

    在C++中,通过类(class)来实现封装,类定义了对象的结构和行为。 2. 继承:允许一个类(子类)从另一个类(父类)继承属性和方法,从而实现代码复用和分类层次结构。C++中的继承关键字是`class SubClass : access...

    windows编程和相应代码实现 多任务编程1_Windows内核对象

    在实际项目中,程序员需要根据具体需求选择合适的内核对象,并正确使用API函数进行操作,如CreateMutex、WaitForSingleObject、ReleaseMutex等。同时,良好的编程习惯和对同步原语的理解,能帮助开发者编写出高效、...

    java并发编程专题(六)----浅析(JUC)Semaphore

    我们创建了一个 Semaphore 对象,并将其设置为 5 个许可。然后,我们使用 ExecutorService 来提交 10 个线程,每个线程都需要获取信号量以访问共享资源。如果信号量可用,则线程可以访问共享资源,否则线程需要等待...

    学习多线程之一:线程通信--利用事件对象.zip_线程通信

    在Java中,我们可以使用java.util.concurrent包下的Semaphore或CountDownLatch等类来模拟类似的功能。这些类可以帮助我们控制线程的执行顺序,确保资源的安全访问。 在"学习多线程之一:线程通信--利用事件对象.zip...

    windows编程 PV操作 直观实现生产者与消费者

    在Windows编程中,我们可以使用CreateSemaphore函数创建一个信号量对象。信号量有两个关键属性:当前计数值和最大值。当生产者完成一个产品并准备放入资源池时,它会执行P操作,即调用WaitForSingleObject函数尝试...

    完整的用PB实现线程管理

    标题中的“完整的用PB实现线程管理”表明我们即将探讨的是如何使用PowerBuilder(PB)这一编程工具来管理和控制线程。线程是操作系统的基本执行单元,它在单个进程中可以并发执行多个任务,极大地提高了程序的运行...

    线程、多线程和线程池面试专题.docx

    1. 使用Semaphore控制线程的并发访问:创建Semaphore变量,Semaphore semaphore = new Semaphore(5,true);当方法进入时,请求一个信号,如果信号被用完则等待,方法运行完,释放一个信号,释放的信号新的线程就可以...

    Windows 完整PING功能实现

    为了保证并发安全,这两个类可能都涉及到线程同步机制,如互斥量(Mutex)、信号量(Semaphore)或事件对象(Event),确保在多线程环境下对共享资源的正确访问。例如,当多个线程尝试同时访问和修改`NetTerminal`的...

    哲学家就餐问题(C#实现)

    在C#中实现哲学家就餐问题,我们可以利用.NET Framework提供的多线程支持,如`System.Threading`命名空间中的`Thread`、`Mutex`、`Semaphore`等类。以下是实现该问题的一些关键知识点: 1. **面向对象编程**:哲学...

    基础库代码实现

    5. **对象引用计数**:C++中的智能指针(shared_ptr, weak_ptr等)使用引用计数技术管理对象生命周期,避免内存泄漏。引用计数需要谨慎处理循环引用问题。 6. **内存池**:内存池是一种内存分配策略,通过预先分配...

Global site tag (gtag.js) - Google Analytics