AQS(AbstractQueuedSynchronizer)应用案例-02
1.概述
通过对AQS源码的熟悉,我们可以通过实现AQS实现自定义的锁来加深认识。
2.实现
1.首先我们确定目标是实现一个独占模式的锁,当其中一个线程获得资源时,其他线程再来请求,让它进入队列进行公平的等待。于是,我们用 Sync 代表子类,来实现 AbstractQueuedSynchronizer
其中 tryAcquire 是们在尝试获取资源时,通过子类来判断是否成功的方法,这里需要我们自己实现(父类有默认的实现方法,是因为不需要每个子类都同时实现共享和非共享模式时获取资源的方法)。
因为是独占锁,我们便用 state = 0 为锁无人使用, state = 1 为 锁被占用,arg参数用来表示当前获取资源的个数,独占锁只能获取一个,便先校验是否为1,接着通过CAS尝试改变state的值为1,如果成功则返回true,失败返回 false
@Override
protected boolean tryAcquire(int arg) {
assert arg == 1;
boolean success = false;
if (compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
success = true;
}
return success;
}
同样的,释放锁时,我们也只能释放一把锁,因为释放锁不会出现竞争,因此直接将state设为0即可。 setExclusiveOwnerThread 是一个设置当前持有锁的线程的引用,当释放锁时,需要将持有锁的当前线程设置为null
@Override
protected boolean tryRelease(int arg) {
assert arg == 1;
if(arg == 0){
throw new IllegalMonitorStateException();
}
setState(0);
setExclusiveOwnerThread(null);
return true;
}
isHeldExclusively 用来判断是否是当前线程持有锁,这里可以进行一个 == 判断即可
@Override
protected boolean isHeldExclusively() {
return Thread.currentThread() == getExclusiveOwnerThread();
}
3.完整源码如下
同步锁工具类,静态内部类Sync 实现 AbstractQueuedSynchronizer
public class CusLock {
public CusLock() {
sync = new Sync();
}
public void lock(){
sync.lock();
}
public void unlock(){
sync.unlock();
}
private final Sync sync;
static class Sync extends AbstractQueuedSynchronizer{
void lock(){
if (compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
}else {
acquire(1);
}
}
void unlock(){
release(1);
}
@Override
protected boolean tryAcquire(int arg) {
assert arg == 1;
boolean success = false;
if (compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
success = true;
}
return success;
} @Override
protected boolean tryRelease(int arg) {
assert arg == 1;
if(arg == 0){
throw new IllegalMonitorStateException();
}
setState(0);
setExclusiveOwnerThread(null);
return true;
} @Override
protected boolean isHeldExclusively() {
return Thread.currentThread() == getExclusiveOwnerThread();
}
}
}
1. SynOrder 中 getOrder()方法中用lock.lock() 来占有锁,
2. 打印线程相关信息
3. 睡眠5秒
4. 释放锁
public class SynOrder {
private CusLock lock = new CusLock(); public void getOrderNo() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "--" + SimpleDateFormat.getTimeInstance(SimpleDateFormat.FULL).format(new Date()));
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}
1.通过开启2个线程来对synOrder中的getOrderNo产生竞争,导致阻塞。可以清楚的见到实际上 用AQS来实现自定义的锁,在已有的concurrent包下已够用
public class Main {
public static void main(String[] args) throws InterruptedException {
SynOrder synOrder = new SynOrder();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synOrder.getOrderNo();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synOrder.getOrderNo();
}
});
System.out.println("t1:"+t1.getName());
System.out.println("t2:"+t2.getName());
t1.start();
t2.start();
Thread.currentThread().join();
}
}
4.总结
在一些内置锁无法满足需求的情况下,ReentrantLock 可以作为一种高级工具。当需要吸血高级功能时才应该使用ReentrantLock,这些功能包括:可定时的,可轮询的、可中断的锁获取操作,公平队列、以及非块结构的锁。否则还是应该优先使用 synchronized.
最新文章
- git-入门
- Struts2 源码分析——Action代理类的工作
- WPF 动态更换样式文件
- tyvj1098[luogu 2365]任务安排 batch
- PHP--字符串处理函数
- 一段时间没上来了,看到有很多网友对OWA感兴趣,因为所在公司发展方向的原因,没有太多时间继续深入研究OWA,敬请见谅
- BZOJ1075 : [SCOI2007]最优驾车drive
- Android 自定义ActionBar
- 关于MIM金属注射成型技术知识大全
- SQL Server 2005 To Oracle
- java开发规范总结_命名规范
- python-整理-面向对象
- 隐藏input的光标
- pycharm的安装及用法
- requests爆破+简单数字验证码
- Jquery 强大的表单验证操作
- DEV SIT UAT PET SIM PRD PROD常见环境英文缩写含义
- EF(EntityFramework)与mysql使用,取数据报错,linq实体映射错误
- Redis for Windows
- Linux学习笔记(第十一章)