全称是 AbstractQueuedSynchronizer(抽象队列同步器),是阻塞式锁和相关的同步器工具的框架。
state 属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取 锁和释放锁
- getState - 获取 state 状态
- setState - 设置 state 状态
- compareAndSetState - cas 机制设置 state 状态
- 独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源
提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList
条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet
子类主要实现这样一些方法(默认抛出 UnsupportedOperationException)
tryAcquire
tryRelease
tryAcquireShared
tryReleaseShared
sHeldExclusively
获取锁的方式
// 如果获取锁失败
if (!tryAcquire(arg)) {
// 入队, 可以选择阻塞当前线程 park unpark
}
释放锁的姿势
// 如果释放锁成功
if (tryRelease(arg)) {
// 让阻塞线程恢复运行
}
AQS自定义锁
@Slf4j
public class TestAqs{
public static void main(String[] args) {
MyLock lock = new MyLock();
new Thread(()->{
lock.lock();;
try{
log.debug("locking...");
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
log.debug("unlocking...");
lock.unlock();
}
},"t1").start();
new Thread(()->{
lock.lock();;
try{
log.debug("locking...");
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
log.debug("unlocking...");
lock.unlock();
}
},"t2").start();
}
}
class MyLock implements Lock {
class MySync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
//加上了锁,并设置owner为当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
setExclusiveOwnerThread(null);
setState(0);// volitate的变量放在后面
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
public Condition newCondition() {
return new ConditionObject();
}
}
private MySync sync = new MySync();
//加锁
@Override
public void lock() {
sync.acquire(1);
}
// 加锁,可打断
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// 尝试加锁
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
10:05:56.714 [t1] DEBUG - locking...
10:05:57.725 [t1] DEBUG - unlocking...
10:05:57.725 [t2] DEBUG - locking...
10:05:58.739 [t2] DEBUG - unlocking...
要是在线程内重复加锁
lock.lock();;
log.debug("haha");
lock.lock();;
10:06:37.552 [t1] DEBUG- haha
线程在第三行加第二次锁的时候堵塞住
当读操作远远高于写操作时,这时候使用 读写锁 让 读-读 可以并发,提高性能。
class DataContainer {
private Object data;
private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
private ReentrantReadWriteLock.ReadLock r = rw.readLock();
private ReentrantReadWriteLock.WriteLock w = rw.writeLock();
public Object read() {
log.debug("获取读锁...");
r.lock();
try {
log.debug("读取");
sleep(1);
return data;
} finally {
log.debug("释放读锁...");
r.unlock();
}
}
public void write() {
log.debug("获取写锁...");
w.lock();
try {
log.debug("写入");
sleep(1);
} finally {
log.debug("释放写锁...");
w.unlock();
}
}
}
public class TestAqs {
public static void main(String[] args) {
DataContainer dataContainer = new DataContainer();
new Thread(() -> {
try {
dataContainer.read();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "t1").start();
new Thread(() -> {
try {
dataContainer.read();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "t2").start();
}
}
10:49:21.859 [t1] DEBUG - 获取读锁...
10:49:21.859 [t2] DEBUG - 获取读锁...
10:49:21.864 [t1] DEBUG - 读取
10:49:21.864 [t2] DEBUG - 读取
10:49:22.873 [t1] DEBUG - 释放读锁...
10:49:22.873 [t2] DEBUG - 释放读锁...
测试读锁——读锁可以并发
测试 读锁-写锁 相互阻塞
为了进一步优化读性能,它的特点是在使用读锁、写锁时都必须配合【戳】使用
加解读锁
long stamp = lock.readLock();
lock.unlockRead(stamp);
加解写锁
long stamp = lock.writeLock();
lock.unlockWrite(stamp);
乐观读,StampedLock 支持 tryOptimisticRead() 方法(乐观读),读取完毕后需要做一次 戳校验 如果校验通 过,表示这期间确实没有写操作,数据可以安全使用,如果校验没通过,需要重新获取读锁,保证数据安全。
long stamp = lock.tryOptimisticRead();
// 验戳
if(!lock.validate(stamp)){
// 锁升级
}
用来进行线程同步协作,等待所有线程完成倒计时。
其中构造参数用来初始化等待计数值,await() 用来等待计数归零,countDown() 用来让计数减一
P176