park和unpark
java">LockSupport.park();// 暂停当前线程
LockSupport.unpark(暂停线程对象);// 恢复某个线程的运行
unpark也可以在park之前调用,调用之后,对应的线程中如果后面执行了park,并不会停止
// 原理
每个线程都会关联一个Parker对象,Parker对象由三部分组成(_counter,_cond,_mutex)
调用park时:
_counter为0:则就进入_cond休息,将_counter再次置为0,线程停止运行
_counter为1,不需要休息,将_counter置为0,继续运行
调用unpark时:
线程在_cond中休息,将_counter变为1,将线程唤醒,线程将_counter变为0,继续向下运行
线程在运行,将_counter变为1,线程照常继续运行
java">Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();// 让当前线程停下来
log.debug("unpark...");
log.debug("打断状态: {}", Thread.currentThread().isInterrupted());
// 打断标记为真时,park无效,会继续向下运行
LockSupport.park();
log.debug("unpark...");
});
t1.start();
TimeUnit.SECONDS.sleep(1);
t1.interrupt();// 让park的线程恢复运行
AQS原理
ReentrantLock
CountDownLatch
ReentrantReadWriteLock
Semaphore
等都是基于AQS的
# AbstractQueuedSynchronizer.java
# 提供了一个模板用于实现(依赖于FIFO等待队列的阻塞锁和一些同步器)
# AQS解决了实现一个synchronizer的大量细节
# Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that
# rely on first-in-first-out (FIFO) wait queues.
state属性表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁
独占模式:只能有一个线程访问资源
共享模式:可以有多个线程访问资源
AQS的实现依赖内部的双向队列,如果当前线程竞争锁失败,AQS会把当前线程以及等待状态信息封装成一个Node加入到同步队列中
同时再阻塞该线程,当获取锁的线程释放锁以后,会从队列中唤醒一个阻塞的节点(线程)
等待队列是"CLH"锁队列的一种变体
# The wait queue is a variant of a "CLH" lock queue.
CLH锁通常用于自旋锁
# CLH locks are normally used for spinlocks.
相反,我们将它们用于阻塞同步器,但是使用相同的基本策略,将关于线程的一些控制信息保存在其节点的前任中
# We instead use them for blocking synchronizers, but use the same basic tactic of holding
# some of the control information about a thread in the predecessor of its node.
java">// 实现一个独占锁,需要子类实现
protected boolean tryAcquire(int arg) // state改为1,true表示加锁成功,将当前线程置为Owner线程
protected boolean tryRelease(int arg) // state改为0,Owner线程置为null
protected boolean isHeldExclusively()
// AQS中的acquire方法是加独占锁,内部会调用tryAcquire
// This method can be used to implement method {@link Lock#lock}.
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// AQS中的release方法是释放独占锁,内部会调用tryRelease
// This method can be used to implement method {@link Lock#unlock}.
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒等待队列中线程
return true;
}
return false;
}
自定义的不可重入独占锁
java">@Slf4j
public class Singleton{
public static void main(String[] args) {
MyLock lock = new MyLock();
new Thread(() -> {
lock.lock();
try {
log.debug("lock...");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
log.debug("unlock...");
lock.unlock();
}
},"t1").start();
new Thread(() -> {
lock.lock();
try {
log.debug("lock...");
} finally {
log.debug("unlock...");
lock.unlock();
}
},"t2").start();
}
}
final class MySync extends AbstractQueuedSynchronizer {
@Override// 尝试获取锁
protected boolean tryAcquire(int acquires) {
if (acquires == 1){
if (compareAndSetState(0, 1)) {// 将state置为1(原子性)
setExclusiveOwnerThread(Thread.currentThread());// 将当前线程设置为owner线程
return true;
}
}
return false;
}
@Override// 尝试释放锁
protected boolean tryRelease(int acquires) {
if(acquires == 1) {
if(getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
return false;
}
protected Condition newCondition() {
return new ConditionObject();
}
@Override// 是否持有锁
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
class MyLock implements Lock {
static 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();
}
}
java">// tryAcquireShared
尝试获取资源.负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源
//tryReleaseShared
尝试释放资源,成功则返回true,失败则返回 false
AQS详解
太难了,以后慢慢看吧