闭锁、信号量、栅栏

本文详细介绍了Java并发编程中的同步工具类,包括闭锁CountDownLatch、信号量Semaphore、栅栏CyclicBarrier以及双发栅栏Exchanger。闭锁用于控制线程的启动和结束,信号量用于控制资源的并发访问,栅栏则用于线程间的等待与同步。Exchanger则专注于线程间的数据交换。这些工具在多线程编程中起到关键作用,能有效协调线程的执行流程。
摘要由CSDN通过智能技术生成

一. 同步工具类

同步容器、并发容器可以保证容器内数据被正确访问,但是线程访问流还是不受制约的。同步工具类产生的原因就是协调线程的控制流

某种程度上来说,阻塞队列既是并发容器类,也是同步工具类。阻塞队列对缓存读取、写入的线程操作进行了延时阻塞,也达到了控制线程访问流的目的。但是阻塞队列终究是从容器角度出发,对和容器无关的其他线程流并未做很好的限制。因而同步工具类有了其发挥的余地。

常用的同步工具类包括闭锁、信号量和栅栏


二. 闭锁(CountDownLatch)

闭锁通常使用一个计数器,表示需要等待事件的数量。CountDownLatch是最常见灵活的闭锁实现:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
	public long plannedSchedule(int nThreads, final Runnable schedule) throws InterruptedException{
		final CountDownLatch startFlag = new CountDownLatch(1);
		final CountDownLatch endFlag = new CountDownLatch(nThreads);
		
		for(int i=0; i < nThreads; i++) {
			Thread t = new Thread() {
				public void run() {
					try {
						startFlag.await();
						try {
						schedule.run();
						}finally {
							endFlag.countDown();
						}
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			t.start();
		}
		
		long startTime = System.nanoTime();
		startFlag.countDown();
		endFlag.await();
		long endTime = System.nanoTime();
		return endTime-startTime;
	}
}

首先新建CountDownLatch对象,申请一个计数器初始值。调用countDown()函数会递减闭锁值,表示一个事件发生了;调用await()函数会查询该计数器,若为0表示所有等待事件都已发生,若不为0则一直阻塞到计数器为0(除非等待中的线程中断,或者超过等待时间)。

使用闭锁的原因是希望新开线程全部同时启动运行。否则,线程启动时间是根据调用start()的先后顺序决定,先启动的线程将“领先”后启动的线程。那么startFlag控制主线程同时“放行”所有的工作线程,endFlag则控制主线程只关注最后一个线程执行完成(不同线程执行不同Runnable耗时不同)。如果没有endFlag则主线程要轮询所有工作线程,顺序地等待每个线程执行完成。


三. 信号量(Semaphore)

计数信号量用于控制某个特定资源的操作数量。可以想象Semaphore计数器为虚拟许可(permit),虚拟许可的数量通过初始化构造函数指定。

1. 线程在执行操作时,先调用acquire( )函数获得许可(计数器自减1);如果此时许可没有剩余(计数器为0)则阻塞到有许可为止(除非被中断或者操作过时)。

2. 线程执行完后,调用release( )函数释放许可(计数器自增1);

3. 初始值为1的信号量称作二值信号量(只有可能0、1),可以用作不可重入的“互斥锁”。

可重入锁:以线程为粒度的锁,比如当前线程已持有锁不用重复申请。

不可重入锁:不可重入,以私有对象为粒度的锁,因为该对象的唯一性,线程即使持有锁仍须释放、再申请。

不可重入锁可以表达为下列例子:

public class ReentrantLockDemo {
	private boolean isLocked = false;
	
	public synchronized void lock() throws InterruptedException{
		while(isLocked) {
			wait();
		}
		isLocked = true;
	} 
	
	public synchronized void unlock() {
		isLocked = false;
		notify();
	}
}

 可重入锁以线程为粒度,可以表达为下列例子:


public class UnReentrantLockDemo {
	private boolean isLocked = false;
	Thread lockBy = null;
	int l
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值