Java中高级核心知识全面解析——AQS(原理、什么是Semaphore(信号量)、CountDownLatch(倒计时器)、CyclicB

本文详细解析了Java并发编程中的核心组件AQS(AbstractQueuedSynchronizer),包括AQS的基本原理、共享资源的两种方式(独占与共享)、模板方法模式的应用,以及基于AQS实现的Semaphore、CountDownLatch和CyclicBarrier。通过实例展示了它们的使用场景和关键方法,帮助读者深入理解Java并发控制机制。
摘要由CSDN通过智能技术生成

一、AQS 简单介绍

AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。
image
AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLockSemaphore,其他的诸如ReentrantReadWriteLockSynchronousQueueFutureTask(jdk1.7) 等等皆是基于AQS的。当然,我们自己也能利用 AQS 非常轻松容易地构造出符合我们自己需求的同步器。

二、AQS原理

在面试中被问到并发知识的时候,大多都会被问到“请你说一下自己对于 AQS 原理的理解”。下面给大家一个示例供大家参考,面试不是背题,大家一定要加入自己的思想,即使加入不了自己的思想也要保证自己能够通俗的讲出来而不是背出来。

下面大部分内容其实在AQS类注释上已经给出了,不过是英语看着比较吃力一点,感兴趣的话可以看看源码。

1.AQS原理概览

AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。

看个AQS(AbstractQueuedSynchronizer)原理图:
image
AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。
AQS使用CAS对该同步状态进行原子操作实现对其值的修改。

private volatile int state;//共享变量,使用volatile修饰保证线程可见性

状态信息通过protected类型getStatesetStatecompareAndSetState进行操作

//返回同步状态的当前值 
protected final int getState() {
    
		return state; 
}
// 设置同步状态的值 
protected final void setState(int newState) {
    
		state = newState; 
}
//原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值) 
protected final boolean compareAndSetState(int expect, int update) {
    
		return unsafe.compareAndSwapInt(this, stateOffset, expect, update); 
}

2. AQS对资源的共享方式

AQS 定义两种资源共享方式

1)Exclusive(独占)

只有一个线程能执行,如ReentrantLock。又可分为公平锁非公平锁,ReentrantLock同时支持两种锁,下面以ReentrantLock对这两种锁的定义做介绍:

  • 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
  • 非公平锁:当线程要获取锁时,先通过两次CAS操作去抢锁,如果没抢到,当前线程再加入到队列中等待唤醒。

下面来看ReentrantLock中相关的源代码:
ReentrantLock默认采用非公平锁,因为考虑获得更好的性能,通过boolean来决定是否用公平锁(传入true用公平锁)。

/** Synchronizer providing all implementation mechanics */ 
private final Sync sync; 
public ReentrantLock() {
    
		// 默认非公平锁 
		sync = new NonfairSync(); 
}
public ReentrantLock(boolean fair) {
    
		sync = fair ? new FairSync() : new NonfairSync(); 
}

ReentrantLock中公平锁的lock方法

static final class FairSync extends Sync {
    
	final void lock() {
    
		acquire(1); 
	}
	// AbstractQueuedSynchronizer.acquire(int arg) 
	public final void acquire(int arg) {
    
		if (!tryAcquire(arg) && 
			acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
			selfInterrupt(); 
	}
	protected final boolean tryAcquire(int acquires) {
    
		final Thread current = Thread.currentThread(); 
		int c = getState(); 
		if (c == 0) {
   
			// 1. 和非公平锁相比,这里多了一个判断:是否有线程在等待 
			if (!hasQueuedPredecessors() && 
				compareAndSetState(0, acquires)) {
   
				setExclusiveOwnerThread(current); 
				return true; 
			} 
	}
	else if (current == getExclusiveOwnerThread()) {
    
		int nextc = c + acquires; 
		if (nextc < 0) 
			throw new Error("Maximum lock count exceeded"); 
		setState(nextc); 
		return true; 
	}
	return false; 
	} 
}

非公平锁的 lock 方法:

static final class NonfairSync extends Sync {
    
	final void lock() {
    
		// 2. 和公平锁相比,这里会直接先进行一次CAS,成功就返回了 
		if (compareAndSetState(0, 1)) 
			setExclusiveOwnerThread(Thread.currentThread()); 
			else
				acquire(1); 
}
// AbstractQueuedSynchronizer.acquire(int arg) 
public final void acquire(int arg) {
    
	if (!tryAcquire(arg) && 
		acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
		selfInterrupt(); 
}
protected final boolean tryAcquire(int acquires) {
    
	return nonfairTryAcquire(acquires); 
	} 
}
/**
 1. Performs non-fair tryLock. tryAcquire is implemented in 
 2. subclasses, but both need nonfair try for trylock method. 
*/ 
final boolean nonfairTryAcquire(int acquires) {
    
	final Thread current = Thread.currentThread(); 
	
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值